<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>面向对象 | Keith&#39;s blog</title>
    <meta name="generator" content="VuePress 1.8.0">
    <link rel="icon" href="/img/favicon.png">
    <script data-ad-client="ca-pub-7828333725993554" async="async" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <meta name="description" content="web前端技术博客,简洁至上,专注web前端学习与总结。JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github等技术文章。">
    <meta name="keywords" content="前端博客,个人技术博客,前端,前端开发,前端框架,web前端,前端面试题,技术文档,学习,面试,JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github,markdown">
    <meta name="baidu-site-verification" content="7F55weZDDc">
    <meta name="theme-color" content="#11a8cd">
    <link rel="preload" href="/assets/css/0.styles.95a28cfa.css" as="style"><link rel="preload" href="/assets/js/app.ae5bf724.js" as="script"><link rel="preload" href="/assets/js/2.72b09a26.js" as="script"><link rel="preload" href="/assets/js/3.96877024.js" as="script"><link rel="preload" href="/assets/js/134.a455c276.js" as="script"><link rel="prefetch" href="/assets/js/10.3e431465.js"><link rel="prefetch" href="/assets/js/100.c7b84f24.js"><link rel="prefetch" href="/assets/js/101.e5a7e6f5.js"><link rel="prefetch" href="/assets/js/102.85992487.js"><link rel="prefetch" href="/assets/js/103.3f187c57.js"><link rel="prefetch" href="/assets/js/104.103d7471.js"><link rel="prefetch" href="/assets/js/105.df0063c0.js"><link rel="prefetch" href="/assets/js/106.d3f285ee.js"><link rel="prefetch" href="/assets/js/107.7ca5e73a.js"><link rel="prefetch" href="/assets/js/108.b5e1495a.js"><link rel="prefetch" href="/assets/js/109.12b39840.js"><link rel="prefetch" href="/assets/js/11.c0c2ac1e.js"><link rel="prefetch" href="/assets/js/110.2996b4eb.js"><link rel="prefetch" href="/assets/js/111.752c7e87.js"><link rel="prefetch" href="/assets/js/112.7c01a356.js"><link rel="prefetch" href="/assets/js/113.240fbeb5.js"><link rel="prefetch" href="/assets/js/114.502fae11.js"><link rel="prefetch" href="/assets/js/115.28ee11f8.js"><link rel="prefetch" href="/assets/js/116.a3f220c8.js"><link rel="prefetch" href="/assets/js/117.293dbd7c.js"><link rel="prefetch" href="/assets/js/118.7fe06c79.js"><link rel="prefetch" href="/assets/js/119.eb539041.js"><link rel="prefetch" href="/assets/js/12.0ea0beb2.js"><link rel="prefetch" href="/assets/js/120.8502bd78.js"><link rel="prefetch" href="/assets/js/121.696d358f.js"><link rel="prefetch" href="/assets/js/122.a629ef96.js"><link rel="prefetch" href="/assets/js/123.bc97cb45.js"><link rel="prefetch" href="/assets/js/124.e9dbc4a1.js"><link rel="prefetch" href="/assets/js/125.3372248a.js"><link rel="prefetch" href="/assets/js/126.58bf8057.js"><link rel="prefetch" href="/assets/js/127.532c80ac.js"><link rel="prefetch" href="/assets/js/128.a8a9dd63.js"><link rel="prefetch" href="/assets/js/129.952e36b7.js"><link rel="prefetch" href="/assets/js/13.81b38e3a.js"><link rel="prefetch" href="/assets/js/130.3f453493.js"><link rel="prefetch" href="/assets/js/131.15bb01bf.js"><link rel="prefetch" href="/assets/js/132.5e501da7.js"><link rel="prefetch" href="/assets/js/133.fbd296a8.js"><link rel="prefetch" href="/assets/js/135.50985645.js"><link rel="prefetch" href="/assets/js/136.dd156bdc.js"><link rel="prefetch" href="/assets/js/137.91d77519.js"><link rel="prefetch" href="/assets/js/138.af96bbc9.js"><link rel="prefetch" href="/assets/js/139.ff60e390.js"><link rel="prefetch" href="/assets/js/14.13ff5f93.js"><link rel="prefetch" href="/assets/js/140.51ef13cd.js"><link rel="prefetch" href="/assets/js/141.2c36f0bb.js"><link rel="prefetch" href="/assets/js/142.745a5a9d.js"><link rel="prefetch" href="/assets/js/143.ef12ce5a.js"><link rel="prefetch" href="/assets/js/144.d53b8a10.js"><link rel="prefetch" href="/assets/js/145.01c80734.js"><link rel="prefetch" href="/assets/js/146.ff1abf8c.js"><link rel="prefetch" href="/assets/js/147.b334a956.js"><link rel="prefetch" href="/assets/js/148.82d1c504.js"><link rel="prefetch" href="/assets/js/149.4872ca41.js"><link rel="prefetch" href="/assets/js/15.ba6da9d5.js"><link rel="prefetch" href="/assets/js/150.eaa1b09e.js"><link rel="prefetch" href="/assets/js/151.b9deeab6.js"><link rel="prefetch" href="/assets/js/152.8914ff06.js"><link rel="prefetch" href="/assets/js/153.1c968910.js"><link rel="prefetch" href="/assets/js/154.ac2ac8e9.js"><link rel="prefetch" href="/assets/js/155.a41ac4d1.js"><link rel="prefetch" href="/assets/js/156.07ec7daa.js"><link rel="prefetch" href="/assets/js/157.d0cfaeb2.js"><link rel="prefetch" href="/assets/js/158.4a3844de.js"><link rel="prefetch" href="/assets/js/159.cd2739bf.js"><link rel="prefetch" href="/assets/js/16.bd936cd0.js"><link rel="prefetch" href="/assets/js/160.8ea34b7b.js"><link rel="prefetch" href="/assets/js/161.4bb2ece8.js"><link rel="prefetch" href="/assets/js/162.a4b2690b.js"><link rel="prefetch" href="/assets/js/163.85acbc57.js"><link rel="prefetch" href="/assets/js/164.acce92e2.js"><link rel="prefetch" href="/assets/js/165.08b51ce6.js"><link rel="prefetch" href="/assets/js/166.7ad057e5.js"><link rel="prefetch" href="/assets/js/167.2d35a768.js"><link rel="prefetch" href="/assets/js/168.b7790709.js"><link rel="prefetch" href="/assets/js/169.11b5000c.js"><link rel="prefetch" href="/assets/js/17.1d7263eb.js"><link rel="prefetch" href="/assets/js/170.d93daf1f.js"><link rel="prefetch" href="/assets/js/171.14a45c02.js"><link rel="prefetch" href="/assets/js/172.c62eb70d.js"><link rel="prefetch" href="/assets/js/173.9dd4cb5e.js"><link rel="prefetch" href="/assets/js/174.deca226f.js"><link rel="prefetch" href="/assets/js/175.eef20df3.js"><link rel="prefetch" href="/assets/js/176.c09f5a25.js"><link rel="prefetch" href="/assets/js/177.99e3a6a8.js"><link rel="prefetch" href="/assets/js/178.22bd595d.js"><link rel="prefetch" href="/assets/js/179.ef63471d.js"><link rel="prefetch" href="/assets/js/18.9412ae71.js"><link rel="prefetch" href="/assets/js/180.306daca4.js"><link rel="prefetch" href="/assets/js/181.9f8eae15.js"><link rel="prefetch" href="/assets/js/182.36cd1ffa.js"><link rel="prefetch" href="/assets/js/183.0195ae23.js"><link rel="prefetch" href="/assets/js/184.61363002.js"><link rel="prefetch" href="/assets/js/185.d01ed7e5.js"><link rel="prefetch" href="/assets/js/186.915e1946.js"><link rel="prefetch" href="/assets/js/187.392993c9.js"><link rel="prefetch" href="/assets/js/188.afcb3c55.js"><link rel="prefetch" href="/assets/js/189.99313768.js"><link rel="prefetch" href="/assets/js/19.17a0f886.js"><link rel="prefetch" href="/assets/js/190.3740a1c8.js"><link rel="prefetch" href="/assets/js/191.a8cebbb8.js"><link rel="prefetch" href="/assets/js/192.a4e4b0eb.js"><link rel="prefetch" href="/assets/js/193.f87069af.js"><link rel="prefetch" href="/assets/js/194.a83aa1ad.js"><link rel="prefetch" href="/assets/js/195.6cccc323.js"><link rel="prefetch" href="/assets/js/196.0ca74fc2.js"><link rel="prefetch" href="/assets/js/197.e57085c4.js"><link rel="prefetch" href="/assets/js/198.9d8c4195.js"><link rel="prefetch" href="/assets/js/199.e34f6d16.js"><link rel="prefetch" href="/assets/js/20.751f715d.js"><link rel="prefetch" href="/assets/js/200.4f0c1baf.js"><link rel="prefetch" href="/assets/js/201.92b5153f.js"><link rel="prefetch" href="/assets/js/202.9f5b1117.js"><link rel="prefetch" href="/assets/js/203.84c5aa3f.js"><link rel="prefetch" href="/assets/js/204.950ec43f.js"><link rel="prefetch" href="/assets/js/205.3caa1fd5.js"><link rel="prefetch" href="/assets/js/206.c5d73eeb.js"><link rel="prefetch" href="/assets/js/207.f00a7726.js"><link rel="prefetch" href="/assets/js/208.49394867.js"><link rel="prefetch" href="/assets/js/209.f891c646.js"><link rel="prefetch" href="/assets/js/21.7e3b1dd6.js"><link rel="prefetch" href="/assets/js/210.9b3ce6fe.js"><link rel="prefetch" href="/assets/js/211.22cbc362.js"><link rel="prefetch" href="/assets/js/212.5380a60a.js"><link rel="prefetch" href="/assets/js/213.64691857.js"><link rel="prefetch" href="/assets/js/214.bf089249.js"><link rel="prefetch" href="/assets/js/215.285eccf0.js"><link rel="prefetch" href="/assets/js/216.e135c2e1.js"><link rel="prefetch" href="/assets/js/217.202ec57b.js"><link rel="prefetch" href="/assets/js/218.c1b5175f.js"><link rel="prefetch" href="/assets/js/219.ac0461eb.js"><link rel="prefetch" href="/assets/js/22.67350c09.js"><link rel="prefetch" href="/assets/js/220.944daf1d.js"><link rel="prefetch" href="/assets/js/221.73cba4fb.js"><link rel="prefetch" href="/assets/js/222.cd29efdd.js"><link rel="prefetch" href="/assets/js/223.5c40831a.js"><link rel="prefetch" href="/assets/js/224.0a05890e.js"><link rel="prefetch" href="/assets/js/225.7c20df0a.js"><link rel="prefetch" href="/assets/js/226.c7f69539.js"><link rel="prefetch" href="/assets/js/227.9ccb8852.js"><link rel="prefetch" href="/assets/js/228.77bd4f2e.js"><link rel="prefetch" href="/assets/js/229.1c656481.js"><link rel="prefetch" href="/assets/js/23.308ebe55.js"><link rel="prefetch" href="/assets/js/230.a3adae2f.js"><link rel="prefetch" href="/assets/js/231.f40d7e85.js"><link rel="prefetch" href="/assets/js/232.762cdd7e.js"><link rel="prefetch" href="/assets/js/233.29242686.js"><link rel="prefetch" href="/assets/js/24.2bb08ee4.js"><link rel="prefetch" href="/assets/js/25.3f1e32c9.js"><link rel="prefetch" href="/assets/js/26.6d06f7bc.js"><link rel="prefetch" href="/assets/js/27.edfd52de.js"><link rel="prefetch" href="/assets/js/28.8c959146.js"><link rel="prefetch" href="/assets/js/29.1621b6ed.js"><link rel="prefetch" href="/assets/js/30.751b1f17.js"><link rel="prefetch" href="/assets/js/31.ec665a74.js"><link rel="prefetch" href="/assets/js/32.1b1d72d5.js"><link rel="prefetch" href="/assets/js/33.b5bd40a4.js"><link rel="prefetch" href="/assets/js/34.2155b0a7.js"><link rel="prefetch" href="/assets/js/35.d59b534a.js"><link rel="prefetch" href="/assets/js/36.44a9c35e.js"><link rel="prefetch" href="/assets/js/37.fd11aa80.js"><link rel="prefetch" href="/assets/js/38.a788fd7b.js"><link rel="prefetch" href="/assets/js/39.0099a8f6.js"><link rel="prefetch" href="/assets/js/4.ff489266.js"><link rel="prefetch" href="/assets/js/40.40c37c27.js"><link rel="prefetch" href="/assets/js/41.a1008003.js"><link rel="prefetch" href="/assets/js/42.5b767b44.js"><link rel="prefetch" href="/assets/js/43.3f71078e.js"><link rel="prefetch" href="/assets/js/44.ad24f6c3.js"><link rel="prefetch" href="/assets/js/45.25e40b16.js"><link rel="prefetch" href="/assets/js/46.b64da983.js"><link rel="prefetch" href="/assets/js/47.17f22f05.js"><link rel="prefetch" href="/assets/js/48.98ac4a14.js"><link rel="prefetch" href="/assets/js/49.2d4cfb58.js"><link rel="prefetch" href="/assets/js/5.90f8b8b3.js"><link rel="prefetch" href="/assets/js/50.961c11a7.js"><link rel="prefetch" href="/assets/js/51.c69cd332.js"><link rel="prefetch" href="/assets/js/52.63e40184.js"><link rel="prefetch" href="/assets/js/53.ed393af3.js"><link rel="prefetch" href="/assets/js/54.e8bf699e.js"><link rel="prefetch" href="/assets/js/55.8c4b1f29.js"><link rel="prefetch" href="/assets/js/56.3ea6fad9.js"><link rel="prefetch" href="/assets/js/57.e355b934.js"><link rel="prefetch" href="/assets/js/58.591ee455.js"><link rel="prefetch" href="/assets/js/59.f75a544d.js"><link rel="prefetch" href="/assets/js/6.01e8382f.js"><link rel="prefetch" href="/assets/js/60.941e9812.js"><link rel="prefetch" href="/assets/js/61.4f56972b.js"><link rel="prefetch" href="/assets/js/62.bd0a67cc.js"><link rel="prefetch" href="/assets/js/63.5525dd5a.js"><link rel="prefetch" href="/assets/js/64.2e40b93d.js"><link rel="prefetch" href="/assets/js/65.7d7a1250.js"><link rel="prefetch" href="/assets/js/66.e88829f6.js"><link rel="prefetch" href="/assets/js/67.233ad823.js"><link rel="prefetch" href="/assets/js/68.037f0252.js"><link rel="prefetch" href="/assets/js/69.d5c49911.js"><link rel="prefetch" href="/assets/js/7.67b2aba3.js"><link rel="prefetch" href="/assets/js/70.039de8ef.js"><link rel="prefetch" href="/assets/js/71.fbbf266f.js"><link rel="prefetch" href="/assets/js/72.2cbdccb2.js"><link rel="prefetch" href="/assets/js/73.f824da30.js"><link rel="prefetch" href="/assets/js/74.c8e2bd49.js"><link rel="prefetch" href="/assets/js/75.2b326b41.js"><link rel="prefetch" href="/assets/js/76.6d52ed9a.js"><link rel="prefetch" href="/assets/js/77.6c219b3f.js"><link rel="prefetch" href="/assets/js/78.e3a6099c.js"><link rel="prefetch" href="/assets/js/79.847c1106.js"><link rel="prefetch" href="/assets/js/8.27cd446a.js"><link rel="prefetch" href="/assets/js/80.8fda2f41.js"><link rel="prefetch" href="/assets/js/81.c64976c1.js"><link rel="prefetch" href="/assets/js/82.44648201.js"><link rel="prefetch" href="/assets/js/83.a02ab71e.js"><link rel="prefetch" href="/assets/js/84.da4c143b.js"><link rel="prefetch" href="/assets/js/85.1e5083cb.js"><link rel="prefetch" href="/assets/js/86.373e4575.js"><link rel="prefetch" href="/assets/js/87.b7dae2d7.js"><link rel="prefetch" href="/assets/js/88.40e11959.js"><link rel="prefetch" href="/assets/js/89.711c7107.js"><link rel="prefetch" href="/assets/js/9.a4f4c6a7.js"><link rel="prefetch" href="/assets/js/90.36268f74.js"><link rel="prefetch" href="/assets/js/91.0b92ab60.js"><link rel="prefetch" href="/assets/js/92.9813b046.js"><link rel="prefetch" href="/assets/js/93.fcd128dd.js"><link rel="prefetch" href="/assets/js/94.c099f487.js"><link rel="prefetch" href="/assets/js/95.8042bfa3.js"><link rel="prefetch" href="/assets/js/96.45fc745f.js"><link rel="prefetch" href="/assets/js/97.32326e64.js"><link rel="prefetch" href="/assets/js/98.82035585.js"><link rel="prefetch" href="/assets/js/99.b24b3ac3.js">
    <link rel="stylesheet" href="/assets/css/0.styles.95a28cfa.css">
  </head>
  <body class="theme-mode-light">
    <div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu have-body-img"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/img/EB-logo.png" alt="Keith's blog" class="logo"> <span class="site-name can-hide">Keith's blog</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><a href="/pages/294b0a/" class="nav-link">个人总结</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="来自大佬的文章" class="dropdown-title"><!----> <span class="title" style="display:;">来自大佬的文章</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8143cc480faf9a11/" class="nav-link">前端文章</a></li><li class="dropdown-subitem"><a href="/note/javascript/" class="nav-link">学习笔记</a></li></ul></li><li class="dropdown-item"><h4>页面</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-subitem"><a href="/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></li><li class="dropdown-item"><h4>技术</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-subitem"><a href="/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-subitem"><a href="/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-subitem"><a href="/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></li><li class="dropdown-item"><h4>更多</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-subitem"><a href="/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-subitem"><a href="/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-subitem"><a href="/pages/baaa02/" class="nav-link">实用技巧</a></li></ul></li><li class="dropdown-item"><!----> <a href="/about/" class="nav-link">关于</a></li><li class="dropdown-item"><!----> <a href="/pages/beb6c0bd8a66cea6/" class="nav-link">收藏</a></li><li class="dropdown-item"><h4>索引</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/categories/" class="nav-link">分类</a></li><li class="dropdown-subitem"><a href="/tags/" class="nav-link">标签</a></li><li class="dropdown-subitem"><a href="/archives/" class="nav-link">归档</a></li></ul></li></ul></div></div> <a href="https://github.com/yangkeith/vuepress-theme-vdoing" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar" style="display:none;"><div class="blogger"><img src="/img/avatar.jpg"> <div class="blogger-info"><h3>Keith</h3> <span>无名小卒</span></div></div> <nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">首页</a></div><div class="nav-item"><a href="/pages/294b0a/" class="nav-link">个人总结</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="来自大佬的文章" class="dropdown-title"><!----> <span class="title" style="display:;">来自大佬的文章</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8143cc480faf9a11/" class="nav-link">前端文章</a></li><li class="dropdown-subitem"><a href="/note/javascript/" class="nav-link">学习笔记</a></li></ul></li><li class="dropdown-item"><h4>页面</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-subitem"><a href="/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></li><li class="dropdown-item"><h4>技术</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-subitem"><a href="/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-subitem"><a href="/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-subitem"><a href="/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></li><li class="dropdown-item"><h4>更多</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-subitem"><a href="/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-subitem"><a href="/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-subitem"><a href="/pages/baaa02/" class="nav-link">实用技巧</a></li></ul></li><li class="dropdown-item"><!----> <a href="/about/" class="nav-link">关于</a></li><li class="dropdown-item"><!----> <a href="/pages/beb6c0bd8a66cea6/" class="nav-link">收藏</a></li><li class="dropdown-item"><h4>索引</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/categories/" class="nav-link">分类</a></li><li class="dropdown-subitem"><a href="/tags/" class="nav-link">标签</a></li><li class="dropdown-subitem"><a href="/archives/" class="nav-link">归档</a></li></ul></li></ul></div></div> <a href="https://github.com/yangkeith/vuepress-theme-vdoing" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><a href="/pages/0796ba76b4b55368/" class="sidebar-link">基础</a></li><li><a href="/pages/74d2ab3fbfeaaa68/" class="sidebar-link">内置对象</a></li><li><a href="/pages/659b5af5e2e704e0/" aria-current="page" class="active sidebar-link">面向对象</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#一、实例对象与new命令" class="sidebar-link">一、实例对象与new命令</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_1、对象是什么" class="sidebar-link">1、对象是什么</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_2、构造函数" class="sidebar-link">2、构造函数</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_3、new命令" class="sidebar-link">3、new命令</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_4、object-create-创建实例对象" class="sidebar-link">4、Object.create()创建实例对象</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#二、this关键字" class="sidebar-link">二、this关键字</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_1、涵义" class="sidebar-link">1、涵义</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_2、实质" class="sidebar-link">2、实质</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_3、使用场合" class="sidebar-link">3、使用场合</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_4、使用注意点" class="sidebar-link">4、使用注意点</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_5、绑定-this-的方法" class="sidebar-link">5、绑定 this 的方法</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#三、对象的继承" class="sidebar-link">三、对象的继承</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_1、原型对象概述" class="sidebar-link">1、原型对象概述</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_2、instanceof-运算符" class="sidebar-link">2、instanceof 运算符</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_3、构造函数的继承" class="sidebar-link">3、构造函数的继承</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_4、多重继承" class="sidebar-link">4、多重继承</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_5、模块" class="sidebar-link">5、模块</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#四、object-对象的相关方法" class="sidebar-link">四、Object 对象的相关方法</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_1、object-getprototypeof-获取原型对象" class="sidebar-link">1、Object.getPrototypeOf() 获取原型对象</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_2、object-setprototypeof-设置原型对象" class="sidebar-link">2、Object.setPrototypeOf() 设置原型对象</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_3、-object-create-创建实例对象-指向目标对象的原型" class="sidebar-link">3、 Object.create() 创建实例对象，指向目标对象的原型</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型" class="sidebar-link">4、Object.prototype.isPrototypeOf()判断某个对象是否为参数对象的原型</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_5、object-prototype-proto-返回该对象的原型-可读写" class="sidebar-link">5、Object.prototype.__proto__ 返回该对象的原型，可读写</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_6、获取原型对象方法的比较" class="sidebar-link">6、获取原型对象方法的比较</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_7、object-getownpropertynames" class="sidebar-link">7、Object.getOwnPropertyNames()</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_8、object-prototype-hasownproperty" class="sidebar-link">8、Object.prototype.hasOwnProperty()</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_9、in-运算符和-for-in-循环" class="sidebar-link">9、in 运算符和 for...in 循环</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_10、对象的拷贝" class="sidebar-link">10、对象的拷贝</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#五、严格模式" class="sidebar-link">五、严格模式</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_1、设计目的" class="sidebar-link">1、设计目的</a></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#_2、启用方法" class="sidebar-link">2、启用方法</a></li></ul></li><li class="sidebar-sub-header"><a href="/pages/659b5af5e2e704e0/#文档" class="sidebar-link">文档</a></li></ul></li><li><a href="/pages/d61b1cb4cdac1f63/" class="sidebar-link">异步操作</a></li><li><a href="/pages/7d961b8030c6099e/" class="sidebar-link">DOM</a></li><li><a href="/pages/10b2761db5a8e089/" class="sidebar-link">事件</a></li><li><a href="/pages/bab4930124ad2c10/" class="sidebar-link">浏览器模型</a></li></ul> <div class="sidebar-slot sidebar-slot-bottom"><!-- 正方形 -->
      <ins class="adsbygoogle"
          style="display:block"
          data-ad-client="ca-pub-7828333725993554"
          data-ad-slot="3508773082"
          data-ad-format="auto"
          data-full-width-responsive="true"></ins>
      <script>
          (adsbygoogle = window.adsbygoogle || []).push({});
      </script></div></aside> <div><main class="page"><div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-70a2d273><div class="articleInfo" data-v-70a2d273><ul class="breadcrumbs" data-v-70a2d273><li data-v-70a2d273><a href="/" title="首页" class="iconfont icon-home router-link-active" data-v-70a2d273></a></li> <li data-v-70a2d273><a href="/note/javascript" title="《JavaScript教程》笔记-目录页" data-v-70a2d273>《JavaScript教程》笔记</a></li> <!----> <!----></ul> <div class="info" data-v-70a2d273><div title="作者" class="author iconfont icon-touxiang" data-v-70a2d273><a href="https://github.com/xugaoyi" target="_blank" title="作者" class="beLink" data-v-70a2d273>xugaoyi</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-70a2d273><a href="javascript:;" data-v-70a2d273>2020-01-12</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-content"></div></div></div> <h1><img src="">
          面向对象
        </h1> <div class="page-slot page-slot-top"><!-- 固定100% * 90px可显示，max-height:90px未见显示-->
     <ins class="adsbygoogle"
          style="display:inline-block;width:100%;max-height:90px"
          data-ad-client="ca-pub-7828333725993554"
          data-ad-slot="6625304284"></ins>
      <script>
          (adsbygoogle = window.adsbygoogle || []).push({});
      </script></div> <div class="theme-vdoing-content content__default"><h1 id="面向对象编程"><a href="#面向对象编程" class="header-anchor">#</a> 面向对象编程</h1> <h2 id="一、实例对象与new命令"><a href="#一、实例对象与new命令" class="header-anchor">#</a> 一、实例对象与new命令</h2> <h3 id="_1、对象是什么"><a href="#_1、对象是什么" class="header-anchor">#</a> 1、对象是什么</h3> <p>面向对象编程（Object Oriented Programming，缩写为 OOP）是目前主流的编程范式。它将真实世界各种复杂的关系，抽象为一个个对象，然后由对象之间的分工与合作，完成对真实世界的模拟。</p> <p>每一个对象都是<strong>功能中心</strong>，具有明确分工，可以完成<strong>接受信息、处理数据、发出信息</strong>等任务。对象<strong>可以复用</strong>，通过继承机制还<strong>可以定制</strong>。因此，面向对象编程具有<strong>灵活、代码可复用、高度模块化</strong>等特点，容易维护和开发，比起由一系列函数或指令组成的传统的<strong>过程式编程</strong>（procedural programming），更适合多人合作的<strong>大型软件项目</strong>。</p> <p>那么，“对象”（object）到底是什么？我们从两个层次来理解。</p> <p><strong>（1）对象是单个实物的抽象。</strong></p> <p>一本书、一辆汽车、一个人都可以是对象，一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当<strong>实物被抽象成对象，实物之间的关系就变成了对象之间的关系，从而就可以模拟现实情况，针对对象进行编程。</strong></p> <p><strong>（2）对象是一个容器，封装了属性（property）和方法（method）。</strong></p> <p><strong>属性</strong>是对象的状态，<strong>方法</strong>是对象的行为（完成某种任务）。比如，我们可以把动物抽象为<code>animal</code>对象，使用“属性”记录具体是那一种动物，使用“方法”表示动物的某种行为（奔跑、捕猎、休息等等）。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 对象</span>
    name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span> <span class="token comment">// 属性</span>
    <span class="token function-variable function">interest</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// 方法(行为)</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">'正在喝酒'</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
person<span class="token punctuation">.</span><span class="token function">interest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 张三正在喝酒</span>

<span class="token comment">// 真实世界： 有一个人，名字叫张三，他正在喝酒。</span>
<span class="token comment">// 对象模拟： 一个人抽象为一个对象person，对象是一个容器，内部封装了属性name叫张三，他正在喝酒封装成方法interest</span>

</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><h3 id="_2、构造函数"><a href="#_2、构造函数" class="header-anchor">#</a> 2、构造函数</h3> <p>面向对象编程的第一步，就是要生成对象。前面说过，对象是单个实物的抽象。通常需要一个模板，表示某一类实物的共同特征，然后对象根据这个模板生成。</p> <p>典型的面向对象编程语言（比如 C++ 和 Java），都有“类”（class）这个概念。所谓“类”就是对象的模板，对象就是“类”的实例。但是，JavaScript 语言的对象体系，不是基于“类”的，而是<strong>基于构造函数（constructor）和原型链（prototype）</strong>。</p> <p>JavaScript 语言<strong>使用构造函数（constructor）作为对象的模板</strong>。</p> <p><strong>所谓”构造函数”，就是专门用来生成实例对象的函数</strong>。</p> <p>它就是对象的模板，描述实例对象的基本结构。</p> <p>一个构造函数，可以生成多个实例对象，这些实例对象都有相同的结构。</p> <p>构造函数就是一个普通的函数，但是有自己的特征和用法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>Vehicle</code>就是构造函数。为了与普通函数区别，构造函数名字的第一个字母通常大写。</p> <p>构造函数的特点有两个。</p> <ul><li>函数体内部使用了<code>this</code>关键字，代表了所要生成的对象实例。</li> <li>生成对象的时候，必须使用<code>new</code>命令。</li></ul> <p>下面先介绍<code>new</code>命令。</p> <h3 id="_3、new命令"><a href="#_3、new命令" class="header-anchor">#</a> 3、new命令</h3> <h4 id="基本用法"><a href="#基本用法" class="header-anchor">#</a> 基本用法</h4> <p><code>new</code>命令的作用：<strong>执行构造函数，返回一个实例对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span> <span class="token comment">// this指向实例对象v</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// new 执行一个构造函数，返回一个实例对象给v</span>
v<span class="token punctuation">.</span>price <span class="token comment">// 1000</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码通过<code>new</code>命令，让构造函数<code>Vehicle</code>生成一个实例对象，保存在变量<code>v</code>中。这个新生成的实例对象，从构造函数<code>Vehicle</code>得到了<code>price</code>属性。<code>new</code>命令执行时，构造函数内部的<code>this</code>，就代表了新生成的实例对象，<code>this.price</code>表示实例对象有一个<code>price</code>属性，值是1000。</p> <p>使用<code>new</code>命令时，根据需要，<strong>构造函数也可以接受参数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> p<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// new 命令时，构造函数可以接受参数</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p><code>new</code>命令本身就可以执行构造函数，所以后面的构造函数可以带括号，也<strong>可以不带括号</strong>。下面两行代码是等价的，但是为了表示这里是函数调用，<strong>推荐使用括号</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 推荐的写法</span>
<span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 不推荐的写法</span>
<span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>一个很自然的问题是，如果忘了使用<code>new</code>命令，直接调用构造函数会发生什么事？</p> <p>这种情况下，构造函数就变成了普通函数，并不会生成实例对象。而且由于后面会说到的原因，<code>this</code>这时代表全局对象，将造成一些意想不到的结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token function">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 忘记使用new命令，构造函数变成普通函数，不会生成实例，函数内部this指向window</span>
v <span class="token comment">// undefined   函数为普通函数且没有返回值，所有v为undefined</span>
price <span class="token comment">// 1000  函数内部this指向window， price变成全局属性，等价于window.price</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中，调用<code>Vehicle</code>构造函数时，忘了加上<code>new</code>命令。结果，变量<code>v</code>变成了<code>undefined</code>，而<code>price</code>属性变成了全局变量。因此，应该非常小心，避免不使用<code>new</code>命令、直接调用构造函数。</p> <p>为了保证构造函数必须与<code>new</code>命令一起使用，一个解决办法是，<strong>构造函数内部使用严格模式</strong>，即第一行加上<code>use strict</code>。这样的话，一旦忘了使用<code>new</code>命令，直接调用构造函数就会报错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token string">'use strict'</span><span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span> <span class="token comment">// 严格模式中this不能指向window对象，不加new调用this等于undefined，给undefined添加属性会报错</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// TypeError: Cannot set property '_foo' of undefined</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码的<code>Fubar</code>为构造函数，<code>use strict</code>命令保证了该函数在严格模式下运行。由于<strong>严格模式中，函数内部的<code>this</code>不能指向全局对象</strong>，默认等于<code>undefined</code>，导致不加<code>new</code>调用会报错（JavaScript 不允许对<code>undefined</code>添加属性）。</p> <p><strong>另一个解决办法</strong>，构造函数内部判断是否使用<code>new</code>命令，如果发现没有使用，则直接返回一个实例对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链（），返回布尔值。</span>
  <span class="token comment">// 语法： &lt;实例对象&gt; instanceof &lt;构造函数&gt;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">Fubar</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>  <span class="token comment">// 或 使用 (!new.target) 判断是否使用new命令</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span>foo<span class="token punctuation">,</span> bar<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
<span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中的构造函数，不管加不加<code>new</code>命令，都会得到同样的结果。</p> <h4 id="new-命令的原理"><a href="#new-命令的原理" class="header-anchor">#</a> new 命令的原理</h4> <p>使用<code>new</code>命令时，它后面的函数依次执行下面的步骤。</p> <ol><li><p>创建一个空对象，作为将要返回的实例对象。</p></li> <li><p>将这个空对象的原型，指向构造函数的<code>prototype</code>属性。</p></li> <li><p>将这个空对象赋值给函数内部的<code>this</code>关键字。</p></li> <li><p>开始执行构造函数内部的代码。（代码中this指向空对象（实例对象））</p></li> <li><p>返回实例对象（或自定义对象）</p></li></ol> <p>也就是说，构造函数内部，<code>this</code>指的是一个新生成的空对象，<strong>所有针对<code>this</code>的操作，都会发生在这个空对象上</strong>。构造函数之所以叫“构造函数”，就是说这个函数的目的，就是操作一个空对象（即<code>this</code>对象），将其“构造”为需要的样子。</p> <p>如果构造函数内部有<code>return</code>语句，而且<code>return</code>后面跟着一个对象，<code>new</code>命令会返回<code>return</code>语句指定的对象；否则，就会不管<code>return</code>语句，返回<code>this</code>对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token number">1000</span><span class="token punctuation">;</span> <span class="token comment">// 1000 非对象，被忽略，返回的是this对象；如果是return {}，则会返回{}</span>

<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token number">1000</span>
<span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，构造函数<code>Vehicle</code>的<code>return</code>语句返回一个数值。这时，<code>new</code>命令就会忽略这个<code>return</code>语句，返回“构造”后的<code>this</code>对象。</p> <p>但是，如果<code>return</code>语句返回的是一个跟<code>this</code>无关的新对象，<code>new</code>命令会返回这个新对象，而不是<code>this</code>对象。这一点需要特别引起注意。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span> price<span class="token operator">:</span> <span class="token number">2000</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// return的是一个对象，会被返回出去。</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>price
<span class="token comment">// 2000</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中，构造函数<code>Vehicle</code>的<code>return</code>语句，返回的是一个新对象。<code>new</code>命令会返回这个对象，而不是<code>this</code>对象。</p> <p>另一方面，如果对普通函数（内部没有<code>this</code>关键字的函数）使用<code>new</code>命令，则会返回一个空对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 内部沒有this关键字，会返回一个空对象</span>
  <span class="token keyword">return</span> <span class="token string">'this is a message'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> msg <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

msg <span class="token comment">// {}</span>
<span class="token keyword">typeof</span> msg <span class="token comment">// &quot;object&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，<code>getMessage</code>是一个普通函数，返回一个字符串。对它使用<code>new</code>命令，会得到一个空对象。这是因为**<code>new</code>命令总是返回一个对象，要么是实例对象，要么是<code>return</code>语句指定的对象**。本例中，<code>return</code>语句返回的是字符串，所以<code>new</code>命令就忽略了该语句。</p> <p><code>new</code>命令简化的内部流程，可以用下面的代码表示。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 构造函数</span>
<span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span>age</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name
    <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age
<span class="token punctuation">}</span>

<span class="token comment">// 自定义_new</span>
<span class="token keyword">function</span> <span class="token function">_new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 将 arguments 对象转为数组</span>
  <span class="token keyword">var</span> args <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 取出构造函数</span>
  <span class="token keyword">var</span> constructor <span class="token operator">=</span> args<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 创建一个空对象，继承构造函数的 prototype 属性</span>
  <span class="token keyword">var</span> context <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>constructor<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 执行构造函数</span>
  <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">constructor</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 如果返回结果是对象，就直接返回，否则返回 context 对象</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> result <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&amp;&amp;</span> result <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token operator">?</span> result <span class="token operator">:</span> context<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 自定义_new2</span>
<span class="token keyword">function</span> <span class="token function">_new2</span><span class="token punctuation">(</span><span class="token comment">/* 构造函数 */</span> constructor<span class="token punctuation">,</span> <span class="token comment">/* 构造函数参数 */</span> params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 创建一个空对象，继承构造函数的 prototype 属性</span>
  <span class="token keyword">var</span> context <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>constructor<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 执行构造函数</span>
  <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">constructor</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> params<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 如果返回结果是对象，就直接返回，否则返回 context 对象</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> result <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&amp;&amp;</span> result <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token operator">?</span> result <span class="token operator">:</span> context<span class="token punctuation">;</span>
  <span class="token comment">// （当用户在构造函数内部自定义返回对象的话则使用该对象，否则返回context）</span>
<span class="token punctuation">}</span>


<span class="token comment">// 通过自定义_new 返回实例</span>
<span class="token keyword">var</span> actor <span class="token operator">=</span> <span class="token function">_new</span><span class="token punctuation">(</span>Person<span class="token punctuation">,</span> <span class="token string">'张三'</span><span class="token punctuation">,</span> <span class="token number">28</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
actor<span class="token punctuation">.</span>name <span class="token comment">// 张三</span>

<span class="token comment">// 通过自定义_new2 返回实例</span>
<span class="token keyword">var</span> actor2 <span class="token operator">=</span> <span class="token function">_new2</span><span class="token punctuation">(</span>Person<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'李四'</span><span class="token punctuation">,</span> <span class="token number">29</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
actor2<span class="token punctuation">.</span>name <span class="token comment">// 李四</span>

<span class="token comment">// 通过new命令 返回实例</span>
<span class="token keyword">var</span> actor3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'王五'</span><span class="token punctuation">,</span><span class="token number">30</span><span class="token punctuation">)</span>
actor3<span class="token punctuation">.</span>name <span class="token comment">// 王五</span>

</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br></div></div><h4 id="new-target-属性"><a href="#new-target-属性" class="header-anchor">#</a> new.target 属性</h4> <p>构造函数内部可以使用<code>new.target</code>属性。如果当前函数是<code>new</code>命令调用，<code>new.target</code><strong>指向当前函数</strong>，否则为<code>undefined</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">new</span><span class="token punctuation">.</span>target <span class="token operator">===</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
<span class="token keyword">new</span> <span class="token class-name">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>使用这个属性，可以<strong>判断函数调用的时候，是否使用<code>new</code>命令</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">new</span><span class="token punctuation">.</span>target<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'请使用 new 命令调用！'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span>

<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Uncaught Error: 请使用 new 命令调用！</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，构造函数<code>f</code>调用时，没有使用<code>new</code>命令，就抛出一个错误。</p> <h3 id="_4、object-create-创建实例对象"><a href="#_4、object-create-创建实例对象" class="header-anchor">#</a> 4、Object.create()创建实例对象</h3> <p>构造函数作为模板，可以生成实例对象。但是，有时拿不到构造函数，只能拿到一个现有的对象。我们希望以这个现有的对象作为模板，生成新的实例对象，这时就可以使用<code>Object.create()</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 这个例子没有构造函数，只有一个对象</span>

<span class="token keyword">var</span> person1 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 这个对象用来生成实例对象，它被当成一个模板</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  age<span class="token operator">:</span> <span class="token number">38</span><span class="token punctuation">,</span>
  <span class="token function-variable function">greeting</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hi! I\'m '</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">'.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> person2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>person1<span class="token punctuation">)</span><span class="token punctuation">;</span>

person2<span class="token punctuation">.</span>name <span class="token comment">// 张三</span>
person2<span class="token punctuation">.</span><span class="token function">greeting</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Hi! I'm 张三.</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>上面代码中，对象<code>person1</code>是<code>person2</code>的模板，后者继承了前者的属性和方法。</p> <p><code>Object.create()</code>的详细介绍，请看后面的相关章节。</p> <h2 id="二、this关键字"><a href="#二、this关键字" class="header-anchor">#</a> 二、this关键字</h2> <h3 id="_1、涵义"><a href="#_1、涵义" class="header-anchor">#</a> 1、涵义</h3> <p><code>this</code>关键字是一个非常重要的语法点。毫不夸张地说，不理解它的含义，大部分开发任务都无法完成。</p> <p>前一章已经提到，<code>this</code>可以用在构造函数之中，表示实例对象。除此之外，<code>this</code>还可以用在别的场合。但不管是什么场合，<code>this</code>都有一个共同点：<strong>它总是返回一个对象</strong>。</p> <p>简单说，<strong><code>this</code>就是属性或方法“当前”所在的对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">this</span><span class="token punctuation">.</span>property <span class="token comment">// this代表property属性当前所在的对象</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码中，<code>this</code>就代表<code>property</code>属性当前所在的对象。</p> <p>下面是一个实际的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  <span class="token function-variable function">describe</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token string">'姓名：'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

person<span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// &quot;姓名：张三&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中，<code>this.name</code>表示<code>name</code>属性所在的那个对象。由于<code>this.name</code>是在<code>describe</code>方法中调用，而<code>describe</code>方法所在的当前对象是<code>person</code>，因此<code>this</code>指向<code>person</code>，<code>this.name</code>就是<code>person.name</code>。</p> <p>由于对象的属性可以赋给另一个对象，所以属性所在的当前对象是可变的，即<code>this</code><strong>的指向是可变的</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  <span class="token function-variable function">describe</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token string">'姓名：'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'李四'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token constant">B</span><span class="token punctuation">.</span>describe <span class="token operator">=</span> <span class="token constant">A</span><span class="token punctuation">.</span>describe<span class="token punctuation">;</span>

<span class="token comment">/* 此时B的值：
B={
	name: '李四',
    describe: function () {
        return '姓名：'+ this.name;
    }
}
*/</span>

<span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// B内部的this指向B对象</span>
<span class="token comment">// &quot;姓名：李四&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br></div></div><p>上面代码中，<code>A.describe</code>属性被赋给<code>B</code>，于是<code>B.describe</code>就表示<code>describe</code>方法所在的当前对象是<code>B</code>，所以<code>this.name</code>就指向<code>B.name</code>。</p> <p>稍稍重构这个例子，<code>this</code>的动态指向就能看得更清楚。</p> <div class="language-JS line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token string">'姓名：'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  describe<span class="token operator">:</span> f
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'李四'</span><span class="token punctuation">,</span>
  describe<span class="token operator">:</span> f
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token constant">A</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名：张三&quot;</span>
<span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名：李四&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p>上面代码中，函数<code>f</code>内部使用了<code>this</code>关键字，随着<code>f</code>所在的对象不同，<code>this</code>的指向也不同。</p> <p>只要函数被赋给另一个变量，<code>this</code>的指向就会变。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  <span class="token function-variable function">describe</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token string">'姓名：'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'李四'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token constant">A</span><span class="token punctuation">.</span>describe<span class="token punctuation">;</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名：李四&quot; f函数中的this和name变量所在的对象都为window顶层对象</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中，<code>A.describe</code>被赋值给变量<code>f</code>，内部的<code>this</code>就会指向<code>f</code>运行时所在的对象（本例是顶层对象）。</p> <p>再看一个网页编程的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token operator">&lt;</span>input type<span class="token operator">=</span><span class="token string">&quot;text&quot;</span> name<span class="token operator">=</span><span class="token string">&quot;age&quot;</span> size<span class="token operator">=</span><span class="token number">3</span> onChange<span class="token operator">=</span><span class="token string">&quot;validate(this, 18, 99);&quot;</span><span class="token operator">&gt;</span>

<span class="token operator">&lt;</span>script<span class="token operator">&gt;</span>
<span class="token keyword">function</span> <span class="token function">validate</span><span class="token punctuation">(</span><span class="token parameter">obj<span class="token punctuation">,</span> lowval<span class="token punctuation">,</span> hival</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>value <span class="token operator">&lt;</span> lowval<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>value <span class="token operator">&gt;</span> hival<span class="token punctuation">)</span><span class="token punctuation">)</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Invalid Value!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码是一个文本输入框，每当用户输入一个值，就会调用<code>onChange</code>回调函数，验证这个值是否在指定范围。浏览器会向回调函数传入当前对象，因此<code>this</code>就代表传入当前对象（即文本框），然后就可以从<code>this.value</code>上面读到用户的输入值。</p> <p>总结一下，<strong>JavaScript 语言之中，一切皆对象，运行环境也是对象，所以函数都是在某个对象之中运行，<code>this</code>就是函数运行时所在的对象（环境）</strong>。这本来并不会让用户糊涂，但是 JavaScript 支持运行环境动态切换，也就是说，<code>this</code>的指向是动态的，没有办法事先确定到底指向哪个对象，这才是最让初学者感到困惑的地方。</p> <h3 id="_2、实质"><a href="#_2、实质" class="header-anchor">#</a> 2、实质</h3> <p>JavaScript 语言之所以有 this 的设计，跟内存里面的数据结构有关系。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> foo<span class="token operator">:</span>  <span class="token number">5</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面的代码将一个对象赋值给变量<code>obj</code>。JavaScript 引擎会先在内存里面，生成一个对象<code>{ foo: 5 }</code>，然后把这个对象的内存地址赋值给变量<code>obj</code>。也就是说，变量<code>obj</code>是一个地址（reference）。后面如果要读取<code>obj.foo</code>，引擎先从<code>obj</code>拿到内存地址，然后再从该地址读出原始的对象，返回它的<code>foo</code>属性。</p> <p>原始的对象以字典结构保存，每一个属性名都对应一个属性描述对象。举例来说，上面例子的<code>foo</code>属性，实际上是以下面的形式保存的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">{</span>
  foo<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token punctuation">[</span>value<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token number">5</span>
    <span class="token punctuation">[</span><span class="token punctuation">[</span>writable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
    <span class="token punctuation">[</span><span class="token punctuation">[</span>enumerable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
    <span class="token punctuation">[</span><span class="token punctuation">[</span>configurable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>注意，<code>foo</code>属性的值保存在属性描述对象的<code>value</code>属性里面。</p> <p>这样的结构是很清晰的，问题在于属性的值可能是一个函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function-variable function">foo</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>这时，<strong>引擎会将函数单独保存在内存中</strong>，然后再将函数的地址赋值给<code>foo</code>属性的<code>value</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">{</span>
  foo<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token punctuation">[</span>value<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> 函数的地址
    <span class="token operator">...</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>由于函数是一个单独的值，所以它可以在不同的环境（上下文）执行。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> f<span class="token operator">:</span> f <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 单独执行</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment">// obj 环境执行</span>
obj<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>JavaScript 允许在函数体内部，引用当前环境的其他变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，函数体里面使用了变量<code>x</code>。该变量由运行环境提供。</p> <p>现在问题就来了，由于函数可以在不同的运行环境执行，所以需要有一种机制，能够在函数体内部获得当前的运行环境（context）。所以，<code>this</code>就出现了，<strong>它的设计目的就是在函数体内部，指代函数当前的运行环境</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，函数体里面的<code>this.x</code>就是指当前运行环境的<code>x</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  f<span class="token operator">:</span> f<span class="token punctuation">,</span>
  x<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 单独执行</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1  f方法当前运行环境为window对象</span>

<span class="token comment">// obj 环境执行</span>
obj<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 2 f方法当前运行环境为obj对象</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中，函数<code>f</code>在全局环境执行，<code>this.x</code>指向全局环境的<code>x</code>；在<code>obj</code>环境执行，<code>this.x</code>指向<code>obj.x</code>。</p> <h3 id="_3、使用场合"><a href="#_3、使用场合" class="header-anchor">#</a> 3、使用场合</h3> <p><code>this</code>主要有以下几个使用场合。</p> <h4 id="_1-全局环境"><a href="#_1-全局环境" class="header-anchor">#</a> （1）全局环境</h4> <p>全局环境使用<code>this</code>，它指的就是顶层对象<code>window</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">this</span> <span class="token operator">===</span> window <span class="token comment">// true</span>

<span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> window<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码说明，不管是不是在函数内部，只要是在全局环境下运行，<code>this</code>就是指顶层对象<code>window</code>。</p> <h4 id="_2-构造函数"><a href="#_2-构造函数" class="header-anchor">#</a> （2）构造函数</h4> <p>构造函数中的<code>this</code>，指的是实例对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Obj</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>p <span class="token operator">=</span> p<span class="token punctuation">;</span> <span class="token comment">// this指向实例对象，在实例对象上定义属性p等于p值</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Obj</span><span class="token punctuation">(</span><span class="token string">'Hello World!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 通过new构造函数来声明实例对象o</span>
o<span class="token punctuation">.</span>p <span class="token comment">// &quot;Hello World!&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码定义了一个构造函数<code>Obj</code>。由于<code>this</code>指向实例对象，所以在构造函数内部定义<code>this.p</code>，就相当于定义实例对象有一个<code>p</code>属性。</p> <h4 id="_3-对象的方法"><a href="#_3-对象的方法" class="header-anchor">#</a> （3）对象的方法</h4> <p>如果对象的方法里面包含<code>this</code>，<code>this</code>的指向就是方法运行时所在的对象。该方法赋值给另一个对象，就会改变<code>this</code>的指向。</p> <p>但是，这条规则很不容易把握。请看下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span><span class="token punctuation">{</span>
  <span class="token function-variable function">foo</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span><span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// obj</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中，<code>obj.foo</code>方法执行时，它内部的<code>this</code>指向<code>obj</code>。</p> <p>但是，下面这几种用法，都会改变<code>this</code>的指向。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 情况一</span>
<span class="token punctuation">(</span>obj<span class="token punctuation">.</span>foo <span class="token operator">=</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>
<span class="token comment">// 情况二</span>
<span class="token punctuation">(</span><span class="token boolean">false</span> <span class="token operator">||</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>
<span class="token comment">// 情况三</span>
<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>

<span class="token comment">//obj.foo 是一个内存地址，它直接取出了 function () {console.log(this);} ，可以把obj.foo看做function () {console.log(this);}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，<code>obj.foo</code>就是一个值。这个值真正调用的时候，运行环境已经不是<code>obj</code>了，而是全局环境，所以<code>this</code>不再指向<code>obj</code>。</p> <p>可以这样理解，JavaScript 引擎内部，<strong><code>obj</code>和<code>obj.foo</code>储存在两个内存地址</strong>，称为地址一和地址二。<code>obj.foo()</code>这样调用时，是从地址一调用地址二，因此地址二的运行环境是地址一，<code>this</code>指向<code>obj</code>。但是，<strong>上面三种情况，都是直接取出地址二进行调用，这样的话，运行环境就是全局环境，因此<code>this</code>指向全局环境</strong>。上面三种情况等同于下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">//obj.foo 是一个内存地址，它直接取出了 function () {console.log(this);} ，可以把obj.foo看做function () {console.log(this);}</span>

<span class="token comment">// 情况一</span>
<span class="token punctuation">(</span>obj<span class="token punctuation">.</span><span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 等同于</span>
<span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment">// 情况二</span>
<span class="token punctuation">(</span><span class="token boolean">false</span> <span class="token operator">||</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment">// 情况三</span>
<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><p>如果<code>this</code>所在的方法不在对象的第一层，这时<code>this</code>只是指向当前一层的对象，而不会继承更上面的层。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  p<span class="token operator">:</span> <span class="token string">'Hello'</span><span class="token punctuation">,</span>
  b<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token function-variable function">m</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

a<span class="token punctuation">.</span>b<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中，<code>a.b.m</code>方法在<code>a</code>对象的第二层，该方法内部的<code>this</code>不是指向<code>a</code>，而是指向<code>a.b</code>，因为实际执行的是下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">m</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  p<span class="token operator">:</span> <span class="token string">'Hello'</span><span class="token punctuation">,</span>
  b<span class="token operator">:</span> b
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token punctuation">(</span>a<span class="token punctuation">.</span>b<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等同于 b.m()</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>如果要达到预期效果，只有写成下面这样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  b<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token function-variable function">m</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    p<span class="token operator">:</span> <span class="token string">'Hello'</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>如果这时将嵌套对象内部的方法赋值给一个变量，<code>this</code>依然会指向全局对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  b<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token function-variable function">m</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    p<span class="token operator">:</span> <span class="token string">'Hello'</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> hello <span class="token operator">=</span> a<span class="token punctuation">.</span>b<span class="token punctuation">.</span>m<span class="token punctuation">;</span> <span class="token comment">// 把方法的内存地址赋值给了hello</span>
<span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined  内部this指向window</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码中，<code>m</code>是多层对象内部的一个方法。为求简便，将其赋值给<code>hello</code>变量，结果调用时，<code>this</code>指向了顶层对象。为了避免这个问题，可以只将<code>m</code>所在的对象赋值给<code>hello</code>，这样调用时，<code>this</code>的指向就不会变。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> hello <span class="token operator">=</span> a<span class="token punctuation">.</span>b<span class="token punctuation">;</span>
hello<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Hello   this指向不变</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><h3 id="_4、使用注意点"><a href="#_4、使用注意点" class="header-anchor">#</a> 4、使用注意点</h3> <h4 id="_1-避免多层-this"><a href="#_1-避免多层-this" class="header-anchor">#</a> （1）避免多层 this</h4> <p>由于<code>this</code>的指向是不确定的，所以切勿在函数中包含多层的<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">f1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this指向o对象</span>
    <span class="token keyword">var</span> <span class="token function-variable function">f2</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 方法会另起一个内存地址</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this指向window对象</span>
    <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// Object</span>
<span class="token comment">// Window</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>上面代码包含两层<code>this</code>，结果运行后，第一层指向对象<code>o</code>，第二层指向全局对象，因为实际执行的是下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">temp</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">f1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> f2 <span class="token operator">=</span> <span class="token function">temp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>一个解决方法是在第二层改用一个指向外层<code>this</code>的变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">f1</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> that <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token comment">// 使用变量保存this</span>
    <span class="token keyword">var</span> <span class="token function-variable function">f2</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>that<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// Object</span>
<span class="token comment">// Object</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码定义了变量<code>that</code>，固定指向外层的<code>this</code>，然后在内层使用<code>that</code>，就不会发生<code>this</code>指向的改变。</p> <p>事实上，使用一个变量固定<code>this</code>的值，然后内层函数调用这个变量，是非常常见的做法，请务必掌握。</p> <p>JavaScript 提供了严格模式，也可以硬性避免这种问题。严格模式下，如果函数内部的<code>this</code>指向顶层对象，就会报错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  count<span class="token operator">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
counter<span class="token punctuation">.</span><span class="token function-variable function">inc</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token string">'use strict'</span><span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> counter<span class="token punctuation">.</span>inc<span class="token punctuation">;</span> <span class="token comment">// 这里直接取出方法的内存地址赋值给f，运行环境为window</span>
<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// TypeError: Cannot read property 'count' of undefined</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中，<code>inc</code>方法通过<code>'use strict'</code>声明采用严格模式，这时内部的<code>this</code>一旦指向顶层对象，就会报错。</p> <h4 id="_2-避免数组处理方法中的-this"><a href="#_2-避免数组处理方法中的-this" class="header-anchor">#</a> （2）避免数组处理方法中的 this</h4> <p>数组的<code>map</code>和<code>foreach</code>方法，允许提供一个函数作为参数。这个函数内部不应该使用<code>this</code> 。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  v<span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  p<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 这个方法的运行环境为window</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// undefined a1</span>
<span class="token comment">// undefined a2</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中，<code>foreach</code>方法的回调函数中的<code>this</code>，其实是指向<code>window</code>对象，因此取不到<code>o.v</code>的值。原因跟上一段的多层<code>this</code>是一样的，就是内层的<code>this</code>不指向外部，而指向顶层对象。</p> <p>解决这个问题的一种方法，就是前面提到的，使用中间变量固定<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  v<span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  p<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">var</span> that <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>that<span class="token punctuation">.</span>v<span class="token operator">+</span><span class="token string">' '</span><span class="token operator">+</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// hello a1</span>
<span class="token comment">// hello a2</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>另一种方法是将<code>this</code>当作<code>foreach</code>方法的第二个参数，固定它的运行环境。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  v<span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  p<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// hello a1</span>
<span class="token comment">// hello a2</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>ES6箭头函数</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  v<span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  p<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token comment">// 箭头函数使this指向为o对象</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v<span class="token operator">+</span><span class="token string">' '</span><span class="token operator">+</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// hello a1</span>
<span class="token comment">// hello a2</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><h4 id="_3-避免回调函数中的-this"><a href="#_3-避免回调函数中的-this" class="header-anchor">#</a> （3）避免回调函数中的 this</h4> <p>回调函数中的<code>this</code>往往会改变指向，最好避免使用。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
o<span class="token punctuation">.</span><span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// jQuery 的写法</span>
<span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> o<span class="token punctuation">.</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中，点击按钮以后，控制台会显示<code>false</code>。原因是此时<code>this</code>不再指向<code>o</code>对象，而是指向按钮的 DOM 对象，因为<code>f</code>方法是在按钮对象的环境中被调用的。这种细微的差别，很容易在编程中忽视，导致难以察觉的错误。</p> <p>为了解决这个问题，可以采用下面的一些方法对<code>this</code>进行绑定，也就是使得<code>this</code>固定指向某个对象，减少不确定性。</p> <h3 id="_5、绑定-this-的方法"><a href="#_5、绑定-this-的方法" class="header-anchor">#</a> 5、绑定 this 的方法</h3> <p><code>this</code>的动态切换，固然为 JavaScript 创造了巨大的灵活性，但也使得编程变得困难和模糊。有时，需要把<code>this</code>固定下来，避免出现意想不到的情况。JavaScript 提供了<code>call</code>、<code>apply</code>、<code>bind</code>这三个方法，来切换/固定<code>this</code>的指向。</p> <h4 id="function-prototype-call"><a href="#function-prototype-call" class="header-anchor">#</a> Function.prototype.call()</h4> <p>函数实例的<code>call</code>方法，可以<strong>指定函数内部<code>this</code>的指向（即函数执行时所在的作用域），然后在所指定的作用域中，调用该函数。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> window <span class="token comment">// true</span>
<span class="token function">f</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token operator">===</span> obj <span class="token comment">// true 使函数内this指向obj，相当于把函数放入obj对象内运行。</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，全局环境运行函数<code>f</code>时，<code>this</code>指向全局环境（浏览器为<code>window</code>对象）；<code>call</code>方法可以改变<code>this</code>的指向，指定<code>this</code>指向对象<code>obj</code>，然后在对象<code>obj</code>的作用域中运行函数<code>f</code>。</p> <p><strong><code>call</code>方法的参数，应该是一个对象</strong>。如果参数为空、<code>null</code>和<code>undefined</code>，则<strong>默认传入全局对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> n <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> n<span class="token operator">:</span> <span class="token number">456</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 参数为空、null、undefined时默认传入全局对象</span>
<span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
<span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
<span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>

<span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span> <span class="token comment">// 123</span>

<span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">// 456</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中，<code>a</code>函数中的<code>this</code>关键字，如果指向全局对象，返回结果为<code>123</code>。如果使用<code>call</code>方法将<code>this</code>关键字指向<code>obj</code>对象，返回结果为<code>456</code>。可以看到，如果<code>call</code>方法没有参数，或者参数为<code>null</code>或<code>undefined</code>，则等同于指向全局对象。</p> <p>如果<code>call</code>方法的参数是一个原始值，那么这个<strong>原始值会自动转成对应的包装对象</strong>，然后传入<code>call</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">f</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span>
<span class="token comment">// Number {[[PrimitiveValue]]: 5} // Number的实例对象</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>call</code>的参数为<code>5</code>，不是对象，会被自动转成包装对象（<code>Number</code>的实例），绑定<code>f</code>内部的<code>this</code>。</p> <p><code>call</code>方法还可以接受多个参数。</p> <h5 id="语法"><a href="#语法" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span>
<span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
<span class="token comment">// 其余参数为传入函数的参数</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p><code>call</code>的第一个参数就是<code>this</code>所要指向的那个对象，后面的参数则是函数调用时所需的参数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">add</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// 3</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，<code>call</code>方法指定函数<code>add</code>内部的<code>this</code>绑定当前环境（对象），并且参数为<code>1</code>和<code>2</code>，因此函数<code>add</code>运行后得到<code>3</code>。</p> <h5 id="应用-调用对象的原生方法"><a href="#应用-调用对象的原生方法" class="header-anchor">#</a> 应用：调用对象的原生方法</h5> <p><code>call</code>方法的一个应用是调用对象的原生方法。</p> <div class="language-JS line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// false 判断obj是否有toString属性，注意这里不是toString()方法</span>

<span class="token comment">// 覆盖掉继承的 hasOwnProperty 方法</span>
obj<span class="token punctuation">.</span><span class="token function-variable function">hasOwnProperty</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// true</span>

<span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> <span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中，<code>hasOwnProperty</code>是<code>obj</code>对象继承的方法，如果这个方法一旦被覆盖，就不会得到正确结果。<code>call</code>方法可以解决这个问题，它将<code>hasOwnProperty</code>方法的原始定义放到<code>obj</code>对象上执行，这样无论<code>obj</code>上有没有同名方法，都不会影响结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> objArr <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 类数组对象</span>
    <span class="token number">0</span><span class="token operator">:</span><span class="token string">'a'</span><span class="token punctuation">,</span>
    <span class="token number">1</span><span class="token operator">:</span><span class="token string">'b'</span><span class="token punctuation">,</span>
    <span class="token number">2</span><span class="token operator">:</span><span class="token string">'c'</span><span class="token punctuation">,</span>
    length<span class="token operator">:</span><span class="token number">3</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> arr <span class="token operator">=</span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>objArr<span class="token punctuation">)</span> <span class="token comment">// 执行数组的slice方法，并把obj指定为方法的this</span>
<span class="token comment">// 或 arr = [].slice.call(obj)</span>

arr <span class="token comment">// ['a','b','c']</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码中，<code>objArr</code>是一个类似数组的对象，使用<code>call</code>调用数组的<code>slice</code>方法，指定<code>objArr</code>为<code>slice</code>方法内部的<code>this</code>，<code>slice</code>方法返回值赋给<code>arr</code>。</p> <p>另外来看看数组<code>slice</code>方法的内部实现原理</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">slice</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">start<span class="token punctuation">,</span>end</span><span class="token punctuation">)</span><span class="token punctuation">{</span>  <span class="token comment">//数组方法slice的底层内部实现</span>
    <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//新数组</span>
    <span class="token keyword">var</span> start <span class="token operator">=</span> start <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token keyword">var</span> end <span class="token operator">=</span> end <span class="token operator">||</span> <span class="token keyword">this</span><span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token comment">//this指向调用的对象，用了call之后，改变this的指向，指向传进来的对象</span>
    <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span>start<span class="token punctuation">;</span> i<span class="token operator">&lt;</span>end<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        result<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> result<span class="token punctuation">;</span>	<span class="token comment">//返回的为一个新的数组</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h4 id="function-prototype-apply"><a href="#function-prototype-apply" class="header-anchor">#</a> Function.prototype.apply()</h4> <p><code>apply</code>方法的作用与<code>call</code>方法类似，也是改变<code>this</code>指向，然后再调用该函数。唯一的区别就是，它接收一个数组作为函数执行时的参数，使用格式如下。</p> <h5 id="语法-2"><a href="#语法-2" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> <span class="token punctuation">[</span>arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
<span class="token comment">// 第二个参数为一个数组，数组每一项为传入函数的参数</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p><code>apply</code>方法的第一个参数也是<code>this</code>所要指向的那个对象，如果设为<code>null</code>或<code>undefined</code>，则等同于指定全局对象。第二个参数则是一个数组，该数组的所有成员依次作为参数，传入原函数。原函数的参数，在<code>call</code>方法中必须一个个添加，但是在<code>apply</code>方法中，必须以数组形式添加。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x <span class="token operator">+</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">f</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
<span class="token function">f</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>f</code>函数本来接受两个参数，使用<code>apply</code>方法以后，就变成可以接受一个数组作为参数。</p> <p>利用这一点，可以做一些有趣的应用。</p> <h5 id="应用"><a href="#应用" class="header-anchor">#</a> 应用：</h5> <h5 id="_1-获取数组最大-最小元素"><a href="#_1-获取数组最大-最小元素" class="header-anchor">#</a> （1）获取数组最大/最小元素</h5> <p>JavaScript 不提供找出数组最大元素的函数。结合使用<code>apply</code>方法和<code>Math.max/Math.min</code>方法，就可以返回数组的最大/最小元素。</p> <p><strong>利用第二个参数为数组的特点</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 获取最大值</span>
<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span> <span class="token comment">// 15</span>

<span class="token comment">//Math.max方法获取最大值</span>
<span class="token comment">//Math.max(10, 2, 4, 15, 9) // 15</span>

<span class="token comment">// 获取最小值</span>
<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
Math<span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span> <span class="token comment">// 15    null等于绑定全局对象</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><h5 id="_2-将数组的空元素变为undefined"><a href="#_2-将数组的空元素变为undefined" class="header-anchor">#</a> （2）将数组的空元素变为<code>undefined</code></h5> <p>通过<code>apply</code>方法，利用<code>Array</code>构造函数将数组的空元素变成<code>undefined</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">Array</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment">// [ 'a', undefined, 'b' ]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>空元素与<code>undefined</code>的差别在于，数组的<code>forEach</code>方法会跳过空元素，但是不会跳过<code>undefined</code>。因此，遍历内部元素的时候，会得到不同的结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token parameter">i</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

a<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>print<span class="token punctuation">)</span>
<span class="token comment">// a</span>
<span class="token comment">// b</span>

<span class="token function">Array</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>print<span class="token punctuation">)</span>
<span class="token comment">// a</span>
<span class="token comment">// undefined</span>
<span class="token comment">// b</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><h5 id="_3-转换类似数组的对象"><a href="#_3-转换类似数组的对象" class="header-anchor">#</a> （3）转换类似数组的对象</h5> <p>另外，利用数组对象的<code>slice</code>方法，可以将一个类似数组的对象（比如<code>arguments</code>对象）转为真正的数组。</p> <p>和使用<code>call</code>方法的效果一样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> length<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
<span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// []</span>
<span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> length<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [1, undefined]</span>
<span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span>length<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [undefined]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码的<code>apply</code>方法的参数都是对象，但是返回结果都是数组，这就起到了将对象转成数组的目的。从上面代码可以看到，这个方法起作用的前提是，被处理的对象必须有<code>length</code>属性，以及相对应的数字键。</p> <h5 id="_4-绑定回调函数的对象"><a href="#_4-绑定回调函数的对象" class="header-anchor">#</a> （4）绑定回调函数的对象</h5> <p>前面的按钮点击事件的例子，可以改写如下。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

o<span class="token punctuation">.</span><span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// true  ，如未改变this指向，this将指向 DOM 对象</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 或者 o.f.call(o);</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// jQuery 的写法</span>
<span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中，点击按钮以后，控制台将会显示<code>true</code>。由于<code>apply</code>方法（或者<code>call</code>方法）不仅绑定函数执行时所在的对象，还会立即执行函数，因此不得不把绑定语句写在一个函数体内。更简洁的写法是采用下面介绍的<code>bind</code>方法。</p> <h4 id="function-prototype-bind"><a href="#function-prototype-bind" class="header-anchor">#</a> Function.prototype.bind()</h4> <p><code>bind</code>方法用于将函数体内的<code>this</code>绑定到某个对象，然后<strong>返回一个新函数，bind方法并非立即执行一个函数。</strong></p> <h5 id="语法-3"><a href="#语法-3" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
<span class="token comment">// 其余参数为传入函数的参数</span>
<span class="token comment">// bind方法返回一个新函数，并非立即执行，如需执行要在后面加个括号</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> d <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
d<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1481869925657</span>

<span class="token keyword">var</span> print <span class="token operator">=</span> d<span class="token punctuation">.</span>getTime<span class="token punctuation">;</span>
<span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Uncaught TypeError: this is not a Date object.</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，我们将<code>d.getTime</code>方法赋给变量<code>print</code>，然后调用<code>print</code>就报错了。这是因为<code>getTime</code>方法内部的<code>this</code>，绑定<code>Date</code>对象的实例，赋给变量<code>print</code>以后，内部的<code>this</code>已经不指向<code>Date</code>对象的实例了。</p> <p><code>bind</code>方法可以解决这个问题。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> print <span class="token operator">=</span> d<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>d<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1481869925657</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码中，<code>bind</code>方法将<code>getTime</code>方法内部的<code>this</code>绑定到<code>d</code>对象，这时就可以安全地将这个方法赋值给其他变量了。</p> <p><code>bind</code>方法的参数就是所要绑定<code>this</code>的对象，下面是一个更清晰的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  count<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  <span class="token function-variable function">inc</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> func <span class="token operator">=</span> counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
counter<span class="token punctuation">.</span>count <span class="token comment">// 1</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中，<code>counter.inc</code>方法被赋值给变量<code>func</code>。这时必须用<code>bind</code>方法将<code>inc</code>内部的<code>this</code>，绑定到<code>counter</code>，否则就会出错。</p> <p><code>this</code>绑定到其他对象也是可以的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  count<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  <span class="token function-variable function">inc</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  count<span class="token operator">:</span> <span class="token number">100</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> func <span class="token operator">=</span> counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span>count <span class="token comment">// 101</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中，<code>bind</code>方法将<code>inc</code>方法内部的<code>this</code>，绑定到<code>obj</code>对象。结果调用<code>func</code>函数以后，递增的就是<code>obj</code>内部的<code>count</code>属性。</p> <p><code>bind</code>还可以接受更多的参数，将这些参数绑定原函数的参数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">add</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> x <span class="token operator">*</span> <span class="token keyword">this</span><span class="token punctuation">.</span>m <span class="token operator">+</span> y <span class="token operator">*</span> <span class="token keyword">this</span><span class="token punctuation">.</span>n<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  m<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
  n<span class="token operator">:</span> <span class="token number">2</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> newAdd <span class="token operator">=</span> <span class="token function">add</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 第二个参数为add内的x</span>
<span class="token function">newAdd</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 20 传入的参数为add内的y</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码中，<code>bind</code>方法除了绑定<code>this</code>对象，还将<code>add</code>函数的第一个参数<code>x</code>绑定成<code>5</code>，然后返回一个新函数<code>newAdd</code>，这个函数只要再接受一个参数<code>y</code>就能运行了。</p> <p>如果<code>bind</code>方法的第一个参数是<code>null</code>或<code>undefined</code>，等于将<code>this</code>绑定到全局对象，函数运行时<code>this</code>指向顶层对象（浏览器为<code>window</code>）。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> x <span class="token operator">+</span> y<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> plus5 <span class="token operator">=</span> <span class="token function">add</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">plus5</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 15  传入的参数为add内的y</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，函数<code>add</code>内部并没有<code>this</code>，使用<code>bind</code>方法的主要目的是绑定参数<code>x</code>，以后每次运行新函数<code>plus5</code>，就只需要提供另一个参数<code>y</code>就够了。而且因为<code>add</code>内部没有<code>this</code>，所以<code>bind</code>的第一个参数是<code>null</code>，不过这里如果是其他对象，也没有影响。</p> <h5 id="bind要注意的点"><a href="#bind要注意的点" class="header-anchor">#</a> bind要注意的点：</h5> <h5 id="_1-每一次返回一个新函数"><a href="#_1-每一次返回一个新函数" class="header-anchor">#</a> （1）每一次返回一个新函数</h5> <p><code>bind</code>方法<strong>每运行一次，就返回一个新函数</strong>，这会产生一些问题。比如，监听事件的时候，不能写成下面这样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>element<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码中，<code>click</code>事件绑定<code>bind</code>方法生成的一个匿名函数。这样会导致无法取消绑定，所以，下面的代码是无效的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>element<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>正确的方法是写成下面这样：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> listener <span class="token operator">=</span> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
element<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//  ...</span>
element<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><h5 id="_2-结合回调函数使用"><a href="#_2-结合回调函数使用" class="header-anchor">#</a> （2）结合回调函数使用</h5> <p>回调函数是 JavaScript 最常用的模式之一，但是一个常见的错误是，将包含<code>this</code>的方法直接当作回调函数。解决方法就是使用<code>bind</code>方法，将<code>counter.inc</code>绑定<code>counter</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  count<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  <span class="token function-variable function">inc</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token string">'use strict'</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">callIt</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">callIt</span><span class="token punctuation">(</span>counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
counter<span class="token punctuation">.</span>count <span class="token comment">// 1</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>上面代码中，<code>callIt</code>方法会调用回调函数。这时如果直接把<code>counter.inc</code>传入，调用时<code>counter.inc</code>内部的<code>this</code>就会指向全局对象。使用<code>bind</code>方法将<code>counter.inc</code>绑定<code>counter</code>以后，就不会有这个问题，<code>this</code>总是指向<code>counter</code>。</p> <p>还有一种情况比较隐蔽，就是某些数组方法可以接受一个函数当作参数。这些函数内部的<code>this</code>指向，很可能也会出错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  times<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token function-variable function">print</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>times<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 数组的forEach方法内部this指向问题</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这里的this指向全局对象</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 没有任何输出</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>上面代码中，<code>obj.print</code>内部<code>this.times</code>的<code>this</code>是指向<code>obj</code>的，这个没有问题。但是，<code>forEach</code>方法的回调函数内部的<code>this.name</code>却是指向全局对象，导致没有办法取到值。稍微改动一下，就可以看得更清楚。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>obj<span class="token punctuation">.</span><span class="token function-variable function">print</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>times<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> window<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// true</span>
<span class="token comment">// true</span>
<span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>解决这个问题，也是通过<code>bind</code>方法绑定<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>obj<span class="token punctuation">.</span><span class="token function-variable function">print</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>times<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 使用bind把this指向的obj对象传入方法内</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 张三</span>
<span class="token comment">// 张三</span>
<span class="token comment">// 张三</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><h5 id="_3-结合call方法使用-改写数组方法调用形式"><a href="#_3-结合call方法使用-改写数组方法调用形式" class="header-anchor">#</a> （3）结合<code>call</code>方法使用（改写数组方法调用形式）</h5> <p>利用<code>bind</code>方法，可以<strong>改写一些 JavaScript 原生方法的使用形式</strong>，以数组的<code>slice</code>方法为例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
<span class="token comment">// 等同于</span>
<span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面的代码中，数组的<code>slice</code>方法从<code>[1, 2, 3]</code>里面，按照指定位置和长度切分出另一个数组。这样做的本质是在<code>[1, 2, 3]</code>上面调用<code>Array.prototype.slice</code>方法，因此可以用<code>call</code>方法表达这个过程，得到同样的结果。</p> <p><code>call</code>方法实质上是调用<code>Function.prototype.call</code>方法，因此上面的表达式可以<strong>用<code>bind</code>方法改写</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> slice <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>slice<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">slice</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码的含义就是，将<code>Array.prototype.slice</code>变成<code>Function.prototype.call</code>方法所在的对象，调用时就变成了<code>Array.prototype.slice.call</code>。类似的写法还可以用于其他数组方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> push <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>push<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> pop <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>pop<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span> <span class="token punctuation">,</span><span class="token number">2</span> <span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token function">push</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span>
a <span class="token comment">// [1, 2, 3, 4]</span>

<span class="token function">pop</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span>
a <span class="token comment">// [1, 2, 3]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>如果再进一步，将<code>Function.prototype.call</code>方法绑定到<code>Function.prototype.bind</code>对象，就意味着<code>bind</code>的调用形式也可以被改写。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span> v<span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> bind <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>bind<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">bind</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> o<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码的含义就是，将<code>Function.prototype.bind</code>方法绑定在<code>Function.prototype.call</code>上面，所以<code>bind</code>方法就可以直接使用，不需要在函数实例上使用。</p> <h4 id="总结-call-、apply-、bind-的区别"><a href="#总结-call-、apply-、bind-的区别" class="header-anchor">#</a> 总结，call()、apply()、bind()的区别</h4> <p>这三个方法都是改变函数内部this指向的。</p> <p>它们的区别是：</p> <p>call()第一个参数为函数内this将要指向的对象，其余参数为传入函数的参数。</p> <p>apply()第一个参数为函数内this将要指向的对象，第二个参数为数组，数组每一项为传入函数的参数。</p> <p>bind()传入参数和call()一样，区别是bind()返回一个新的函数，并非立即执行。</p> <h2 id="三、对象的继承"><a href="#三、对象的继承" class="header-anchor">#</a> 三、对象的继承</h2> <p>面向对象编程很重要的一个方面，就是对象的继承。<strong>A 对象通过继承 B 对象，就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的</strong>。</p> <p>大部分面向对象的编程语言，都是通过“类”（class）实现对象的继承。传统上，JavaScript 语言的继承不通过 class，而是通过“原型对象”（prototype）实现，本章介绍 JavaScript 的原型链继承。</p> <p>ES6 引入了 class 语法，基于 class 的继承不在这个教程介绍，请参阅《ES6 标准入门》一书的相关章节。</p> <p><strong>ES5 是通过“原型对象”（prototype）实现继承。</strong></p> <h3 id="_1、原型对象概述"><a href="#_1、原型对象概述" class="header-anchor">#</a> 1、原型对象概述</h3> <h4 id="_1-构造函数的缺点"><a href="#_1-构造函数的缺点" class="header-anchor">#</a> （1）构造函数的缺点</h4> <p>JavaScript 通过构造函数生成新对象，因此<strong>构造函数可以视为对象的模板</strong>。实例对象的属性和方法，可以定义在构造函数内部。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Cat</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> color</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">,</span> <span class="token string">'白色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

cat1<span class="token punctuation">.</span>name <span class="token comment">// '大毛'</span>
cat1<span class="token punctuation">.</span>color <span class="token comment">// '白色'</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中，<code>Cat</code>函数是一个构造函数，函数内部定义了<code>name</code>属性和<code>color</code>属性，所有实例对象（上例是<code>cat1</code>）都会生成这两个属性，即这两个属性会定义在实例对象上面。</p> <p>通过构造函数为实例对象定义属性，虽然很方便，但是有一个缺点。<strong>同一个构造函数的多个实例之间，无法共享属性，从而造成对系统资源的浪费</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Cat</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> color</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">meow</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'喵喵'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 解决方法，就是在原型对象（prototype）上创建共同的方法</span>
<span class="token class-name">Cat</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">meow2</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'喵喵'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">,</span> <span class="token string">'白色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> cat2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'二毛'</span><span class="token punctuation">,</span> <span class="token string">'黑色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// cat1和cat2是同一个构造函数的两个实例对象，他们无法共享meow属性，从而造成对系统资源的浪费。</span>
cat1<span class="token punctuation">.</span>meow <span class="token operator">===</span> cat2<span class="token punctuation">.</span>meow
<span class="token comment">// false</span>

cat1<span class="token punctuation">.</span>meow2 <span class="token operator">===</span> cat2<span class="token punctuation">.</span>meow2
<span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div><p>上面代码中，<code>cat1</code>和<code>cat2</code>是同一个构造函数的两个实例，它们都具有<code>meow</code>方法。由于<code>meow</code>方法是生成在每个实例对象上面，所以两个实例就生成了两次。也就是说，每新建一个实例，就会新建一个<code>meow</code>方法。这既没有必要，又浪费系统资源，因为所有<code>meow</code>方法都是同样的行为，完全应该共享。</p> <p><strong>这个问题的解决方法，就是 JavaScript 的原型对象（prototype）。</strong></p> <h4 id="_2-prototype-属性的作用"><a href="#_2-prototype-属性的作用" class="header-anchor">#</a> （2）prototype 属性的作用</h4> <p><strong>JavaScript 继承机制的设计思想就是，原型对象的所有属性和方法，都能被实例对象共享</strong>。也就是说，如果属性和方法定义在原型上，那么所有实例对象就能共享，不仅节省了内存，还体现了实例对象之间的联系。</p> <p>下面，先看怎么为对象指定原型。JavaScript 规定，<strong>每个函数都有一个<code>prototype</code>属性，指向一个对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">typeof</span> f<span class="token punctuation">.</span>prototype <span class="token comment">// &quot;object&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码中，函数<code>f</code>默认具有<code>prototype</code>属性，指向一个对象。</p> <p><strong>对于普通函数来说，该属性基本无用</strong>。但是，<strong>对于构造函数来说，生成实例的时候，该属性会自动成为实例对象的原型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Animal</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// Animal.prototype属性是实例cat1和cat2的原型对象，在上面添加属性，实例就共享了该属性</span>
<span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'white'</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Animal</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> cat2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Animal</span><span class="token punctuation">(</span><span class="token string">'二毛'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

cat1<span class="token punctuation">.</span>color <span class="token comment">// 'white'</span>
cat2<span class="token punctuation">.</span>color <span class="token comment">// 'white'</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码中，构造函数<code>Animal</code>的<code>prototype</code>属性，就是实例对象<code>cat1</code>和<code>cat2</code>的原型对象。原型对象上添加一个<code>color</code>属性，结果，实例对象都共享了该属性。</p> <p>原型对象的属性不是实例对象自身的属性。只要修改原型对象，变动就立刻会体现在<strong>所有</strong>实例对象上。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'yellow'</span><span class="token punctuation">;</span>


<span class="token comment">// color属性并非实例对象cat1/cat2本身的属性,只是当实例本身没有该属性或方法时，它会到原型对象去寻找该属性或方法</span>
cat1<span class="token punctuation">.</span>color <span class="token comment">// &quot;yellow&quot;</span>
cat2<span class="token punctuation">.</span>color <span class="token comment">// &quot;yellow&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，原型对象的<code>color</code>属性的值变为<code>yellow</code>，两个实例对象的<code>color</code>属性立刻跟着变了。这是因为实例对象其实没有<code>color</code>属性，都是读取原型对象的<code>color</code>属性。也就是说，**当实例对象本身没有某个属性或方法的时候，它会到原型对象去寻找该属性或方法。**这就是原型对象的特殊之处。</p> <p>如果<strong>实例对象自身就有某个属性或方法，它就不会再去原型对象寻找这个属性或方法</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>cat1<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'black'</span><span class="token punctuation">;</span>

cat1<span class="token punctuation">.</span>color <span class="token comment">// 'black'</span>
cat2<span class="token punctuation">.</span>color <span class="token comment">// 'yellow'</span>
<span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token comment">// 'yellow';</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，实例对象<code>cat1</code>的<code>color</code>属性改为<code>black</code>，就使得它不再去原型对象读取<code>color</code>属性，后者的值依然为<code>yellow</code>。</p> <p>总结一下，原型对象的<strong>作用</strong>，<strong>就是定义所有实例对象共享的属性和方法</strong>。这也是它被称为原型对象的原因，而实例对象可以视作从原型对象衍生出来的子对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">walk</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">' is walking'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>Animal.prototype</code>对象上面定义了一个<code>walk</code>方法，这个方法将可以在所有<code>Animal</code>实例对象上面调用。</p> <h4 id="_3-原型链"><a href="#_3-原型链" class="header-anchor">#</a> （3）原型链</h4> <p><strong>JavaScript 规定，所有对象都有自己的原型对象（prototype）。一方面，任何一个对象，都可以充当其他对象的原型；另一方面，由于原型对象也是对象，所以它也有自己的原型。因此，就会形成一个“原型链”（prototype chain）：对象到原型，再到原型的原型……</strong></p> <p>如果一层层地上溯，所有对象的原型<strong>最终都可以上溯到<code>Object.prototype</code>，即<code>Object</code>构造函数的<code>prototype</code>属性</strong>。也就是说，所有对象都继承了<code>Object.prototype</code>的属性。这就是所有对象都有<code>valueOf</code>和<code>toString</code>方法的原因，因为这是从<code>Object.prototype</code>继承的。</p> <p>那么，<code>Object.prototype</code>对象有没有它的原型呢？回答是<code>Object.prototype</code>的原型是<code>null</code>。<code>null</code>没有任何属性和方法，也没有自己的原型。因此，<strong>原型链的尽头就是<code>null</code></strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> <span class="token comment">// Object.getPrototypeOf方法返回对象的原型</span>
<span class="token comment">// null</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码表示，<code>Object.prototype</code>对象的原型是<code>null</code>，由于<code>null</code>没有任何属性，所以原型链到此为止。<code>Object.getPrototypeOf</code>方法返回参数对象的原型，具体介绍请看后文。</p> <p><strong>读取对象的某个属性时，JavaScript 引擎先寻找对象本身的属性，如果找不到，就到它的原型去找，如果还是找不到，就到原型的原型去找。如果直到最顶层的<code>Object.prototype</code>还是找不到，则返回<code>undefined</code></strong>。如果对象自身和它的原型，都定义了一个同名属性，那么优先读取对象自身的属性，这叫做“覆盖”（overriding）。</p> <p>注意，一级级向上，在整个原型链上寻找某个属性，对性能是有影响的。所寻找的属性在越上层的原型对象，对性能的影响越大。如果寻找某个不存在的属性，将会遍历整个原型链。</p> <p>举例来说，如果让构造函数的<code>prototype</code>属性指向一个数组，就意味着实例对象可以调用数组方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">MyArray</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token class-name">MyArray</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 构造函数MyArray的原型指向 数组实例</span>
<span class="token class-name">MyArray</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> MyArray<span class="token punctuation">;</span>

<span class="token keyword">var</span> mine <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// mine是构造函数MyArray的实例</span>
mine<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mine<span class="token punctuation">.</span>length <span class="token comment">// 3</span>
mine <span class="token keyword">instanceof</span> <span class="token class-name">Array</span> <span class="token comment">// true 判断实例对象mine是否为构造函数Array的实例</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中，<code>mine</code>是构造函数<code>MyArray</code>的实例对象，由于<code>MyArray.prototype</code>指向一个数组实例，使得<code>mine</code>可以调用数组方法（这些方法定义在数组实例的<code>prototype</code>对象上面）。最后那行<code>instanceof</code>表达式，用来比较一个对象是否为某个构造函数的实例，结果就是证明<code>mine</code>为<code>Array</code>的实例，<code>instanceof</code>运算符的详细解释详见后文。</p> <p>上面代码还出现了原型对象的<code>constructor</code>属性，这个属性的含义下一节就来解释。</p> <h4 id="_4-constructor-属性"><a href="#_4-constructor-属性" class="header-anchor">#</a> （4）constructor 属性</h4> <p><strong><code>prototype</code>对象有一个<code>constructor</code>属性，默认指向<code>prototype</code>对象所在的构造函数。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token class-name">P</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">P</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>由于<code>constructor</code>属性定义在<code>prototype</code>对象上面，意味着可以被所有实例对象继承。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

p<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">P</span> <span class="token comment">// true  p自身没有constructor属性，它是读取原型上的</span>
p<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token class-name">P</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token comment">// true</span>
p<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'constructor'</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>p</code>是构造函数<code>P</code>的实例对象，但是<code>p</code>自身没有<code>constructor</code>属性，该属性其实是读取原型链上面的<code>P.prototype.constructor</code>属性。</p> <h5 id="作用"><a href="#作用" class="header-anchor">#</a> 作用</h5> <p><code>constructor</code>属性的<strong>作用</strong>是，<strong>可以得知某个实例对象，到底是哪一个构造函数产生的</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

f<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">F</span> <span class="token comment">// true</span>
f<span class="token punctuation">.</span>constructor <span class="token operator">===</span> RegExp <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，<code>constructor</code>属性确定了实例对象<code>f</code>的构造函数是<code>F</code>，而不是<code>RegExp</code>。</p> <p>另一方面，有了<code>constructor</code>属性，就可以从一个实例对象新建另一个实例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Constr</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Constr</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> y <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">x<span class="token punctuation">.</span>constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 等同于 new Constr()</span>
y <span class="token keyword">instanceof</span> <span class="token class-name">Constr</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，<code>x</code>是构造函数<code>Constr</code>的实例，可以从<code>x.constructor</code>间接调用构造函数。这使得在实例方法中，调用自身的构造函数成为可能。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Constr</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">createCopy</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">this<span class="token punctuation">.</span>constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>createCopy</code>方法调用构造函数，新建另一个实例。</p> <p><code>constructor</code>属性<strong>表示原型对象与构造函数之间的关联关系</strong>，如果修改了原型对象，一般会同时修改<code>constructor</code>属性，防止引用的时候出错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Person <span class="token comment">// true</span>

<span class="token class-name">Person</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 修改了原型对象,但没有修改原型下的constructor</span>
  <span class="token function-variable function">method</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">//  由于原型对象已被修改，原型下的constructor也被修改</span>
<span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Person <span class="token comment">// false</span>
<span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Object <span class="token comment">// true // 普通对象的constructor指向object构造函数</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中，构造函数<code>Person</code>的原型对象改掉了，但是没有修改<code>constructor</code>属性，导致这个属性不再指向<code>Person</code>。由于<code>Person</code>的新原型是一个普通对象，而普通对象的<code>constructor</code>属性指向<code>Object</code>构造函数，导致<code>Person.prototype.constructor</code>变成了<code>Object</code>。</p> <p>所以，修改原型对象时，一般要同时修改<code>constructor</code>属性的指向。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 坏的写法</span>
<span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">method1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 好的写法</span>
<span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  constructor<span class="token operator">:</span> <span class="token constant">C</span><span class="token punctuation">,</span>
  <span class="token function-variable function">method1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 更好的写法 （只是在原型对象上添加方法）</span>
<span class="token class-name">C</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">method1</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中，要么将<code>constructor</code>属性重新指向原来的构造函数，要么只在原型对象上添加方法，这样可以保证<code>instanceof</code>运算符不会失真。</p> <p>如果不能确定<code>constructor</code>属性是什么函数，还有一个办法：通过<code>name</code>属性，从实例得到构造函数的名称。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
f<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>name <span class="token comment">// &quot;Foo&quot;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h3 id="_2、instanceof-运算符"><a href="#_2、instanceof-运算符" class="header-anchor">#</a> 2、instanceof 运算符</h3> <p><code>instanceof</code>运算符，<strong>判断对象是否为某个构造函数的实例，返回一个布尔值</strong>。</p> <h4 id="语法-4"><a href="#语法-4" class="header-anchor">#</a> 语法</h4> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token operator">&lt;</span>实例对象<span class="token operator">&gt;</span> <span class="token keyword">instanceof</span> <span class="token operator">&lt;</span>构造函数<span class="token operator">&gt;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><div class="language- line-numbers-mode"><pre class="language-text"><code>var v = new Vehicle();
v instanceof Vehicle // true   v是构造函数Vehicel的实例
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码中，对象<code>v</code>是构造函数<code>Vehicle</code>的实例，所以返回<code>true</code>。</p> <p><code>instanceof</code>运算符的左边是实例对象，右边是构造函数。它会<strong>检查右边构造函数的原型对象（prototype），是否在左边对象的原型链上</strong>。因此，下面两种写法是等价的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>v <span class="token keyword">instanceof</span> <span class="token class-name">Vehicle</span>
<span class="token comment">// 等同于</span>
<span class="token class-name">Vehicle</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>Object.prototype.isPrototypeOf</code>的详细解释见后文。</p> <p>由于<code>instanceof</code>检查整个原型链，因此同一个实例对象，可能会对多个构造函数都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> d <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
d <span class="token keyword">instanceof</span> <span class="token class-name">Date</span> <span class="token comment">// true</span>
d <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>d</code>同时是<code>Date</code>和<code>Object</code>的实例，因此对这两个构造函数都返回<code>true</code>。</p> <p>由于<strong>任意对象（除了<code>null</code>）都是<code>Object</code>的实例</strong>，所以<code>instanceof</code>运算符可以判断一个值是否为非<code>null</code>的对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> foo<span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
obj <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>

<span class="token keyword">null</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，除了<code>null</code>，其他对象的<code>instanceOf Object</code>的运算结果都是<code>true</code>。</p> <p><code>instanceof</code>的原理是检查右边构造函数的<code>prototype</code>属性，是否在左边对象的原型链上。有一种<strong>特殊情况</strong>，就是左边对象的原型链上，只有<code>null</code>对象。这时，<code>instanceof</code>判断会失真。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">typeof</span> obj <span class="token comment">// &quot;object&quot;</span>
Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，<code>Object.create(null)</code>返回一个新对象<code>obj</code>，它的原型是<code>null</code>（<code>Object.create</code>的详细介绍见后文）。右边的构造函数<code>Object</code>的<code>prototype</code>属性，不在左边的原型链上，因此<code>instanceof</code>就认为<code>obj</code>不是<code>Object</code>的实例。但是，<strong>只要一个对象的原型不是<code>null</code>，<code>instanceof</code>运算符的判断就不会失真</strong>。</p> <h4 id="用处"><a href="#用处" class="header-anchor">#</a> 用处</h4> <p><code>instanceof</code>运算符的一个用处，是<strong>判断值的类型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> y <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> <span class="token function-variable function">z</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
x <span class="token keyword">instanceof</span> <span class="token class-name">Array</span> <span class="token comment">// true</span>
y <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>
y <span class="token keyword">instanceof</span> <span class="token class-name">Function</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>instanceof</code>运算符判断，变量<code>x</code>是数组，变量<code>y</code>是对象，变量<code>z</code>是函数。</p> <p>注意，<code>instanceof</code>运算符<strong>只能用于对象</strong>，<strong>不适用原始类型的值</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> s <span class="token operator">=</span> <span class="token string">'hello'</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> z <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
s <span class="token keyword">instanceof</span> <span class="token class-name">String</span> <span class="token comment">// false</span>
z <span class="token keyword">instanceof</span> <span class="token class-name">Number</span> <span class="token comment">// false</span>
<span class="token comment">// 原始类型并没有实例化，所有都返回false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，字符串不是<code>String</code>对象的实例（因为字符串不是对象），所以返回<code>false</code>。</p> <p>此外，对于<code>undefined</code>和<code>null</code>，<code>instanceof</code>运算符总是返回<code>false</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">undefined</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
<span class="token keyword">null</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>利用<code>instanceof</code>运算符，还可以巧妙地解决，调用构造函数时，<strong>忘了加<code>new</code>命令的问题</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span> <span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">Fubar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 忘加new命令时 this为 全局对象window</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span>foo<span class="token punctuation">,</span> bar<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
<span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码使用<code>instanceof</code>运算符，在函数体内部判断<code>this</code>关键字是否为构造函数<code>Fubar</code>的实例。如果不是，就表明忘了加<code>new</code>命令。</p> <h3 id="_3、构造函数的继承"><a href="#_3、构造函数的继承" class="header-anchor">#</a> 3、构造函数的继承</h3> <p><strong>让一个构造函数继承另一个构造函数</strong>，是非常常见的需求。</p> <p>这可以分成两步实现。<strong>第一步是在子类的构造函数中，调用父类的构造函数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Sub</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Sub是子类构造函数</span>
  <span class="token function">Super</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Super是父类构造函数，这的this是子类的实例</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>prop <span class="token operator">=</span> value<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，<code>Sub</code>是子类的构造函数，<code>this</code>是子类的实例。在实例上调用父类的构造函数<code>Super</code>，<strong>就会让子类实例具有父类实例的属性</strong>。</p> <p><strong>第二步，是让子类的原型指向父类的原型，这样子类就可以继承父类原型。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 子类的原型指向一个新对象，新对象原型指向父类原型，等于子类原型继承了父类原型，且对子类原型操作不会影响到父类原型</span>
<span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Super</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Sub<span class="token punctuation">;</span>
<span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>method <span class="token operator">=</span> <span class="token string">'...'</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，<code>Sub.prototype</code>是子类的原型，要将它赋值为<code>Object.create(Super.prototype)</code>，而不是直接等于<code>Super.prototype</code>。否则后面两行对<code>Sub.prototype</code>的操作，会连父类的原型<code>Super.prototype</code>一起修改掉。</p> <p><strong>另外一种写法</strong>是<code>Sub.prototype</code>等于一个父类实例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 这个写法会继承父类实例的方法，不推荐</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面这种写法也有继承的效果，但是子类会具有父类实例的方法。有时，这可能不是我们需要的，所以<strong>不推荐</strong>使用这种写法。</p> <p>举例来说，下面是一个<code>Shape</code>构造函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Shape</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>y <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token class-name">Shape</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">move</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">+=</span> x<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>y <span class="token operator">+=</span> y<span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">'Shape moved.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>我们需要让<code>Rectangle</code>构造函数继承<code>Shape</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 第一步，子类继承父类的实例</span>
<span class="token keyword">function</span> <span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">Shape</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 调用父类构造函数</span>
<span class="token punctuation">}</span>
<span class="token comment">// 另一种写法</span>
<span class="token keyword">function</span> <span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>base <span class="token operator">=</span> Shape<span class="token punctuation">;</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">base</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 第二步，子类继承父类的原型</span>
<span class="token class-name">Rectangle</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Shape</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Rectangle</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Rectangle<span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>采用这样的写法以后，<code>instanceof</code>运算符会对子类和父类的构造函数，都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> rect <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

rect <span class="token keyword">instanceof</span> <span class="token class-name">Rectangle</span>  <span class="token comment">// true</span>
rect <span class="token keyword">instanceof</span> <span class="token class-name">Shape</span>  <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，子类是整体继承父类。有时只需要<strong>单个方法的继承</strong>，这时可以采用下面的写法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">ClassB</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">print</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 单个方法的继承</span>
  <span class="token class-name">ClassA</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 继承了ClassA的print方法</span>
  <span class="token comment">// some code</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，子类<code>B</code>的<code>print</code>方法先调用父类<code>A</code>的<code>print</code>方法，再部署自己的代码。这就等于继承了父类<code>A</code>的<code>print</code>方法。</p> <h3 id="_4、多重继承"><a href="#_4、多重继承" class="header-anchor">#</a> 4、多重继承</h3> <p>JavaScript 不提供多重继承功能，即不允许一个对象同时继承多个对象。但是，可以通过变通方法，实现这个功能。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">M1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 构造函数M1</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>hello <span class="token operator">=</span> <span class="token string">'hello'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token constant">M2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 构造函数M2</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>world <span class="token operator">=</span> <span class="token string">'world'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token constant">S</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 子类构造函数S</span>
  <span class="token constant">M1</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token constant">M2</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 继承 M1</span>
<span class="token class-name">S</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">M1</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 继承链上加入 M2</span>
Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">S</span><span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> <span class="token class-name">M2</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 指定构造函数</span>
<span class="token class-name">S</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> <span class="token constant">S</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> s <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">S</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
s<span class="token punctuation">.</span>hello <span class="token comment">// 'hello'</span>
s<span class="token punctuation">.</span>world <span class="token comment">// 'world'</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br></div></div><p>上面代码中，子类<code>S</code>同时继承了父类<code>M1</code>和<code>M2</code>。这种模式又称为 Mixin（混入）。</p> <h3 id="_5、模块"><a href="#_5、模块" class="header-anchor">#</a> 5、模块</h3> <p>随着网站逐渐变成“互联网应用程序”，嵌入网页的 JavaScript 代码越来越庞大，越来越复杂。网页越来越像桌面程序，需要一个团队分工协作、进度管理、单元测试等等……开发者必须使用软件工程的方法，管理网页的业务逻辑。</p> <p><strong>JavaScript 模块化编程</strong>，已经成为一个迫切的需求。理想情况下，开发者只需要实现核心的业务逻辑，其他都可以加载别人已经写好的模块。</p> <p>但是，JavaScript 不是一种模块化编程语言，ES6 才开始支持“类”和“模块”。下面介绍传统的做法，如何利用对象实现模块的效果。</p> <h4 id="_1-基本的实现方法"><a href="#_1-基本的实现方法" class="header-anchor">#</a> （1）基本的实现方法</h4> <p><strong>模块是实现特定功能的一组属性和方法的封装。</strong></p> <p>简单的做法是把模块写成一个对象，所有的模块成员都放到这个对象里面。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
　_count <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
　<span class="token function-variable function">m1</span> <span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
　　<span class="token comment">//...</span>
　<span class="token punctuation">}</span><span class="token punctuation">,</span>
　<span class="token function-variable function">m2</span> <span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  　<span class="token comment">//...</span>
　<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面的函数<code>m1</code>和<code>m2</code>，都封装在<code>module1</code>对象里。使用的时候，就是调用这个对象的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 使用</span>
module1<span class="token punctuation">.</span><span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>但是，这样的写法会暴露所有模块成员，内部状态可以被外部改写。比如，外部代码可以直接改变内部计数器的值。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>module1<span class="token punctuation">.</span>_count <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h4 id="_2-封装私有变量"><a href="#_2-封装私有变量" class="header-anchor">#</a> （2）封装私有变量：</h4> <h5 id="_2-1-构造函数的写法"><a href="#_2-1-构造函数的写法" class="header-anchor">#</a> （2-1）构造函数的写法</h5> <p>我们可以利用构造函数，封装私有变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 模块的私有变量</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">add</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
     buffer<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">toString</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> buffer<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>上面代码中，<code>buffer</code>是模块的私有变量。一旦生成实例对象，外部是无法直接访问<code>buffer</code>的。但是，这种方法将私有变量封装在构造函数中，导致构造函数与实例对象是一体的，总是存在于内存之中，无法在使用完成后清除。这意味着，构造函数有双重作用，既用来塑造实例对象，又用来保存实例对象的数据，违背了构造函数与实例对象在数据上相分离的原则（即实例对象的数据，不应该保存在实例对象以外）。同时，非常耗费内存。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token class-name">StringBuilder</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  constructor<span class="token operator">:</span> StringBuilder<span class="token punctuation">,</span>
  <span class="token function-variable function">add</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token function-variable function">toString</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>这种方法将私有变量放入实例对象中，好处是看上去更自然，但是它的私有变量可以从外部读写，不是很安全。</p> <h5 id="_2-2-立即执行函数的写法"><a href="#_2-2-立即执行函数的写法" class="header-anchor">#</a> （2-2）立即执行函数的写法</h5> <p>另一种做法是使用“立即执行函数”（Immediately-Invoked Function Expression，IIFE），将相关的属性和方法封装在一个函数作用域里面，可以达到不暴露私有成员的目的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　<span class="token keyword">var</span> _count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
　<span class="token keyword">var</span> <span class="token function-variable function">m1</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　  <span class="token comment">//...</span>
　<span class="token punctuation">}</span><span class="token punctuation">;</span>
　<span class="token keyword">var</span> <span class="token function-variable function">m2</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　　<span class="token comment">//...</span>
　<span class="token punctuation">}</span><span class="token punctuation">;</span>
　<span class="token keyword">return</span> <span class="token punctuation">{</span>
　　m1 <span class="token operator">:</span> m1<span class="token punctuation">,</span>
　　m2 <span class="token operator">:</span> m2
　<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>使用上面的写法，外部代码无法读取内部的<code>_count</code>变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>module1<span class="token punctuation">.</span>_count<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//undefined</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面的<code>module1</code>就是 JavaScript 模块的基本写法。下面，再对这种写法进行加工。</p> <h4 id="_3-模块的放大模式-向模块添加新方法"><a href="#_3-模块的放大模式-向模块添加新方法" class="header-anchor">#</a> （3）模块的放大模式（向模块添加新方法）</h4> <p>如果一个模块很大，必须分成几个部分，或者一个模块需要继承另一个模块，这时就有必要采用“放大模式”（augmentation）。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">mod</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
　mod<span class="token punctuation">.</span><span class="token function-variable function">m3</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　　<span class="token comment">//...</span>
　<span class="token punctuation">}</span><span class="token punctuation">;</span>
　<span class="token keyword">return</span> mod<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>module1<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面的代码为<code>module1</code>模块添加了一个新方法<code>m3()</code>，然后返回新的<code>module1</code>模块。</p> <p>在浏览器环境中，模块的各个部分通常都是从网上获取的，有时无法知道哪个部分会先加载。如果采用上面的写法，第一个执行的部分有可能加载一个不存在空对象，这时就要采用&quot;宽放大模式&quot;（Loose augmentation）。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">mod</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　<span class="token comment">//...</span>
　<span class="token keyword">return</span> mod<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>module1 <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>与&quot;放大模式&quot;相比，“宽放大模式”就是“立即执行函数”的参数可以是空对象。</p> <h4 id="_4-输入全局变量-保证独立性"><a href="#_4-输入全局变量-保证独立性" class="header-anchor">#</a> （4）输入全局变量（保证独立性）</h4> <p>独立性是模块的重要特点，模块内部最好不与程序的其他部分直接交互。</p> <p>为了在模块内部调用全局变量，必须显式地将其他变量输入模块。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> <span class="token constant">YAHOO</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
　<span class="token comment">//...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> <span class="token constant">YAHOO</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 向模块内部传入全局变量</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面的<code>module1</code>模块需要使用 jQuery 库和 YUI 库，就把这两个库（其实是两个模块）当作参数输入<code>module1</code>。这样做除了保证模块的独立性，还使得模块之间的依赖关系变得明显。</p> <p><strong>立即执行函数还可以起到命名空间的作用</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>

  <span class="token keyword">function</span> <span class="token function">go</span><span class="token punctuation">(</span><span class="token parameter">num</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">function</span> <span class="token function">handleEvents</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">function</span> <span class="token function">initialize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">function</span> <span class="token function">dieCarouselDie</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token punctuation">}</span>

  <span class="token comment">//attach to the global scope</span>
  window<span class="token punctuation">.</span>finalCarousel <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 对外暴露接口</span>
    init <span class="token operator">:</span> initialize<span class="token punctuation">,</span>
    destroy <span class="token operator">:</span> dieCarouselDie
  <span class="token punctuation">}</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span> jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document <span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br></div></div><p>上面代码中，<code>finalCarousel</code>对象输出到全局，对外暴露<code>init</code>和<code>destroy</code>接口，<strong>内部方法<code>go</code>、<code>handleEvents</code>、<code>initialize</code>、<code>dieCarouselDie</code>都是外部无法调用的</strong>。</p> <h2 id="四、object-对象的相关方法"><a href="#四、object-对象的相关方法" class="header-anchor">#</a> 四、Object 对象的相关方法</h2> <p>JavaScript 在<code>Object</code>对象上面，提供了很多相关方法，处理面向对象编程的相关操作。本章介绍这些方法。</p> <h3 id="_1、object-getprototypeof-获取原型对象"><a href="#_1、object-getprototypeof-获取原型对象" class="header-anchor">#</a> 1、Object.getPrototypeOf() 获取原型对象</h3> <p><code>Object.getPrototypeOf</code>方法<strong>返回参数对象的原型</strong>。这是获取原型对象的标准方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">F</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token class-name">F</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中，实例对象<code>f</code>的原型是<code>F.prototype</code>。</p> <p>下面是几种特殊对象的原型。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 空对象的原型是 Object.prototype</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>

<span class="token comment">// Object.prototype 的原型是 null</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token comment">// true</span>

<span class="token comment">// 函数的原型是 Function.prototype</span>
<span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h3 id="_2、object-setprototypeof-设置原型对象"><a href="#_2、object-setprototypeof-设置原型对象" class="header-anchor">#</a> 2、Object.setPrototypeOf() 设置原型对象</h3> <p><code>Object.setPrototypeOf</code>方法为参数对象<strong>设置原型</strong>，<strong>返回该参数对象</strong>。它接受两个参数，第一个是现有对象，第二个是原型对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token punctuation">{</span>x<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>

Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">===</span> b <span class="token comment">// true</span>
a<span class="token punctuation">.</span>x <span class="token comment">// 1  a对象共享b对象的属性</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>Object.setPrototypeOf</code>方法将对象<code>a</code>的原型，设置为对象<code>b</code>，因此<code>a</code>可以共享<code>b</code>的属性。</p> <h5 id="使用object-setprototypeof方法模拟new命令"><a href="#使用object-setprototypeof方法模拟new命令" class="header-anchor">#</a> <strong>使用<code>Object.setPrototypeOf</code>方法模拟<code>new</code>命令</strong></h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">F</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">this</span><span class="token punctuation">.</span>foo <span class="token operator">=</span> <span class="token string">'bar'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 等同于</span>
<span class="token keyword">var</span> f <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token class-name">F</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 模拟new命令</span>
<span class="token constant">F</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，<code>new</code>命令新建实例对象，其实可以分成两步。第一步，将一个空对象的原型设为构造函数的<code>prototype</code>属性（上例是<code>F.prototype</code>）；第二步，将构造函数内部的<code>this</code>绑定这个空对象，然后执行构造函数，使得定义在<code>this</code>上面的方法和属性（上例是<code>this.foo</code>），都转移到这个空对象上。</p> <h3 id="_3、-object-create-创建实例对象-指向目标对象的原型"><a href="#_3、-object-create-创建实例对象-指向目标对象的原型" class="header-anchor">#</a> 3、 Object.create() 创建实例对象，指向目标对象的原型</h3> <p>生成实例对象的常用方法是，使用<code>new</code>命令让构造函数返回一个实例。但是很多时候，只能拿到一个实例对象，它可能根本不是由构建函数生成的，那么能不能从一个实例对象，生成另一个实例对象呢？</p> <p>JavaScript 提供了<code>Object.create</code>方法，用来满足这种需求。**该方法接受一个对象作为参数，然后以它为原型，返回一个实例对象。**该实例完全继承原型对象的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 原型对象</span>
<span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">print</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'hello'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 实例对象</span>
<span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token constant">A</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 以A为原型，创建了B实例对象，使B继承了A的属性</span>

Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token constant">B</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token constant">A</span> <span class="token comment">// true</span>
<span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// hello</span>
<span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token operator">===</span> <span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>上面代码中，<code>Object.create</code>方法以<code>A</code>对象为原型，生成了<code>B</code>对象。<code>B</code>继承了<code>A</code>的所有属性和方法。</p> <p>实际上，<code>Object.create</code>方法可以用下面的代码代替。</p> <h5 id="内部实现原理"><a href="#内部实现原理" class="header-anchor">#</a> 内部实现原理</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> Object<span class="token punctuation">.</span>create <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  Object<span class="token punctuation">.</span><span class="token function-variable function">create</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">obj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 模拟Object.create方法</span>
    <span class="token keyword">function</span> <span class="token constant">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// 创建一个空构造函数F</span>
    <span class="token class-name">F</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> obj<span class="token punctuation">;</span> <span class="token comment">// 让F的原型 指向参数obj（obj为传入的原型对象）</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回一个F的实例</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码表明，<code>Object.create</code>方法的实质是新建一个空的构造函数<code>F</code>，然后让<code>F.prototype</code>属性指向参数对象<code>obj</code>，最后返回一个<code>F</code>的实例，从而实现让该实例继承<code>obj</code>的属性。</p> <p>下面三种方式生成的新对象是等价的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj1 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>如果想要生成一个不继承任何属性（比如没有<code>toString</code>和<code>valueOf</code>方法）的对象，可以将<code>Object.create</code>的参数设为<code>null</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 不继承Object的toString和valueOf方法的一个对象</span>

obj<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// TypeError: Object [object Object] has no method 'valueOf'</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，对象<code>obj</code>的原型是<code>null</code>，它就不具备一些定义在<code>Object.prototype</code>对象上面的属性，比如<code>valueOf</code>方法。</p> <p>使用<code>Object.create</code>方法的时候，必须提供对象原型，即<strong>参数不能为空，或者不是对象，否则会报错</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// TypeError: Object prototype may only be an Object or null</span>
Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span>
<span class="token comment">// TypeError: Object prototype may only be an Object or null</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p><code>Object.create</code>方法生成的新对象，动态继承了原型。在原型上添加或修改任何方法，会立刻反映在新对象之上。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> p<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> obj2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>

obj1<span class="token punctuation">.</span>p <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
obj2<span class="token punctuation">.</span>p <span class="token comment">// 2  obj2的原型指向obj1，当访问obj2上的p属性时，js引擎会先在obj2本身上找，没找到会去原型上找</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中，修改对象原型<code>obj1</code>会影响到实例对象<code>obj2</code>。</p> <p>除了对象的原型，<code>Object.create</code>方法还可以接受<strong>第二个参数。该参数是一个属性描述对象，它所描述的对象属性，会添加到实例对象，作为该对象自身的属性</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  p1<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">// p1为添加到obj实例对象自身的属性</span>
    value<span class="token operator">:</span> <span class="token number">123</span><span class="token punctuation">,</span>
    enumerable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    configurable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    writable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  p2<span class="token operator">:</span> <span class="token punctuation">{</span>
    value<span class="token operator">:</span> <span class="token string">'abc'</span><span class="token punctuation">,</span>
    enumerable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    configurable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    writable<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 等同于</span>
<span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span>p1 <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span>
obj<span class="token punctuation">.</span>p2 <span class="token operator">=</span> <span class="token string">'abc'</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><p><code>Object.create</code>方法生成的对象，<strong>继承了它的原型对象的构造函数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> b <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>

b<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">A</span> <span class="token comment">// true</span>
b <span class="token keyword">instanceof</span> <span class="token class-name">A</span> <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>b</code>对象的原型是<code>a</code>对象，因此继承了<code>a</code>对象的构造函数<code>A</code>。</p> <h3 id="_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型"><a href="#_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型" class="header-anchor">#</a> 4、Object.prototype.isPrototypeOf()判断某个对象是否为参数对象的原型</h3> <p>实例对象的<code>isPrototypeOf</code>方法，用来<strong>判断该对象是否为参数对象的原型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o1 <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> o2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> o3 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o2<span class="token punctuation">)</span><span class="token punctuation">;</span>

o2<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>o3<span class="token punctuation">)</span> <span class="token comment">// true  判断o2是否为o3的原型</span>
o1<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>o3<span class="token punctuation">)</span> <span class="token comment">// true  判断o1是否为o3的原型</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中，<code>o1</code>和<code>o2</code>都是<code>o3</code>的原型。这表明只要实例对象处在参数对象的原型链上，<code>isPrototypeOf</code>方法都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
<span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
<span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">xyz</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span> <span class="token comment">// true</span>
<span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中，由于<code>Object.prototype</code>处于原型链的最顶端，所以对各种实例都返回<code>true</code>，只有直接继承自<code>null</code>的对象除外。</p> <h3 id="_5、object-prototype-proto-返回该对象的原型-可读写"><a href="#_5、object-prototype-proto-返回该对象的原型-可读写" class="header-anchor">#</a> 5、Object.prototype.__proto__ 返回该对象的原型，可读写</h3> <p>实例对象的<code>__proto__</code><strong>属性</strong>（前后各两个下划线），<strong>返回该对象的原型。该属性可读写</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>x<span class="token operator">:</span><span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 创建实例对象obj，其原型指定为{x:1}</span>
obj<span class="token punctuation">.</span>__proto__ <span class="token comment">// {x: 1}  实例对象obj的__proto__属性，返回obj的原型</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">// {x: 1}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码通过<code>Object.create</code>创建实例对象obj，指定其原型为<code>{x:1}</code>，访问obj对象的<code>__proto__</code>属性，返回其原型。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> p<span class="token punctuation">;</span> <span class="token comment">// 原型属性可读写</span>
Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token operator">===</span> p <span class="token comment">// true</span>
obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">//true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码通过<code>__proto__</code>属性，将<code>p</code>对象设为<code>obj</code>对象的原型。</p> <p>根据语言标准，<code>__proto__</code>属性只有浏览器才需要部署，其他环境可以没有这个属性。它前后的两根下划线，表明它本质是一个内部属性，不应该对使用者暴露。<strong>因此，应该尽量少用这个属性，而是用<code>Object.getPrototypeOf()</code>和<code>Object.setPrototypeOf()</code>，进行原型对象的读写操作</strong>。</p> <p>原型链可以用<code>__proto__</code>很直观地表示。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'张三'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  name<span class="token operator">:</span> <span class="token string">'李四'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> proto <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function-variable function">print</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token constant">A</span><span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> proto<span class="token punctuation">;</span> <span class="token comment">// 将A的原型指向proto对象</span>
<span class="token constant">B</span><span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> proto<span class="token punctuation">;</span> <span class="token comment">// 将B的原型指向proto对象</span>

<span class="token comment">// 共享print方法，都是在调用proto对象内的print方法</span>
<span class="token constant">A</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 张三</span>
<span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 李四</span>

<span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token operator">===</span> <span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token comment">// true</span>
<span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token operator">===</span> proto<span class="token punctuation">.</span>print <span class="token comment">// true</span>
<span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token operator">===</span> proto<span class="token punctuation">.</span>print <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br></div></div><p>上面代码中，<code>A</code>对象和<code>B</code>对象的原型都是<code>proto</code>对象，它们都共享<code>proto</code>对象的<code>print</code>方法。也就是说，<code>A</code>和<code>B</code>的<code>print</code>方法，都是在调用<code>proto</code>对象的<code>print</code>方法。</p> <h3 id="_6、获取原型对象方法的比较"><a href="#_6、获取原型对象方法的比较" class="header-anchor">#</a> 6、获取原型对象方法的比较</h3> <p>如前所述，<code>__proto__</code>属性指向当前对象的原型对象，即构造函数的<code>prototype</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype
<span class="token comment">// true</span>
obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> obj<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype
<span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码首先新建了一个对象<code>obj</code>，它的<code>__proto__</code>属性，指向构造函数（<code>Object</code>或<code>obj.constructor</code>）的<code>prototype</code>属性。</p> <p>因此，获取实例对象<code>obj</code>的原型对象，有三种方法。</p> <ul><li><code>obj.__proto__</code></li> <li><code>obj.constructor.prototype</code></li> <li><code>Object.getPrototypeOf(obj)</code></li></ul> <p>上面三种方法之中，前两种都不是很可靠。<code>__proto__</code>属性只有浏览器才需要部署，其他环境可以不部署。而<code>obj.constructor.prototype</code>在手动改变原型对象时，可能会失效。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">P</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> <span class="token function-variable function">C</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> p<span class="token punctuation">;</span>
<span class="token keyword">var</span> c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">C</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

c<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype <span class="token operator">===</span> p <span class="token comment">// false</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中，构造函数<code>C</code>的原型对象被改成了<code>p</code>，但是实例对象的<code>c.constructor.prototype</code>却没有指向<code>p</code>。所以，在改变原型对象时，一般要同时设置<code>constructor</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> p<span class="token punctuation">;</span>
<span class="token class-name">C</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> <span class="token constant">C</span><span class="token punctuation">;</span> <span class="token comment">// 如在构造函数的继承中就使用到这个操作</span>

<span class="token keyword">var</span> c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">C</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
c<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype <span class="token operator">===</span> p <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>因此，<strong>推荐使用第三种<code>Object.getPrototypeOf</code>方法，获取原型对象</strong>。</p> <h3 id="_7、object-getownpropertynames"><a href="#_7、object-getownpropertynames" class="header-anchor">#</a> 7、Object.getOwnPropertyNames()</h3> <p><code>Object.getOwnPropertyNames</code>方法<strong>返回一个数组，成员是参数对象本身的所有属性的键名，不包含继承的属性键名</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span>
<span class="token comment">// [&quot;parse&quot;, &quot;arguments&quot;, &quot;UTC&quot;, &quot;caller&quot;, &quot;name&quot;, &quot;prototype&quot;, &quot;now&quot;, &quot;length&quot;]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码中，<code>Object.getOwnPropertyNames</code>方法返回<code>Date</code>所有自身的属性名。</p> <p>对象本身的属性之中，有的是可以遍历的（enumerable），有的是不可以遍历的。<code>Object.getOwnPropertyNames</code>方法返回所有键名，不管是否可以遍历。只获取那些可以遍历的属性，使用<code>Object.keys</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span> <span class="token comment">// []</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码表明，<code>Date</code>对象所有自身的属性，都是不可以遍历的。</p> <h3 id="_8、object-prototype-hasownproperty"><a href="#_8、object-prototype-hasownproperty" class="header-anchor">#</a> 8、Object.prototype.hasOwnProperty()</h3> <p>对象实例的<code>hasOwnProperty</code>方法返回一个布尔值，用于<strong>判断某个属性定义在对象自身，还是定义在原型链上</strong>。</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码表明，<code>Date.length</code>（构造函数<code>Date</code>可以接受多少个参数）是<code>Date</code>自身的属性，<code>Date.toString</code>是继承的属性。</p> <p>另外，<code>hasOwnProperty</code>方法是 JavaScript 之中唯一一个处理对象属性时，不会遍历原型链的方法。</p> <h3 id="_9、in-运算符和-for-in-循环"><a href="#_9、in-运算符和-for-in-循环" class="header-anchor">#</a> 9、in 运算符和 for...in 循环</h3> <p><code>in</code>运算符<strong>返回一个布尔值，表示一个对象是否具有某个属性</strong>。它不区分该属性是对象自身的属性，还是继承的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token string">'length'</span> <span class="token keyword">in</span> Date <span class="token comment">// true</span>
<span class="token string">'toString'</span> <span class="token keyword">in</span> Date <span class="token comment">// true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p><code>in</code>运算符常用于检查一个属性是否存在。</p> <p>获得对象的所有可遍历属性（不管是自身的还是继承的），可以使用<code>for...in</code>循环。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o1 <span class="token operator">=</span> <span class="token punctuation">{</span> p1<span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> o2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o1<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token comment">// o2的原型指向o1，并且在o2上定义一个属性p2</span>
  p2<span class="token operator">:</span> <span class="token punctuation">{</span> value<span class="token operator">:</span> <span class="token string">&quot;abc&quot;</span><span class="token punctuation">,</span> enumerable<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">for</span> <span class="token punctuation">(</span>p <span class="token keyword">in</span> o2<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// p2</span>
<span class="token comment">// p1    继承的属性</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码中，对象<code>o2</code>的<code>p2</code>属性是自身的，<code>p1</code>属性是继承的。这两个属性都会被<code>for...in</code>循环遍历。</p> <p>为了在<code>for...in</code>循环中获得对象自身的属性，可以采用<code>hasOwnProperty</code>方法判断一下。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> name <span class="token keyword">in</span> object <span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span> object<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 过滤掉非自身的属性</span>
    <span class="token comment">/* loop code */</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p><strong>获得对象的所有属性（不管是自身的还是继承的，也不管是否可枚举）</strong>，可以使用下面的函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">inheritedPropertyNames</span><span class="token punctuation">(</span><span class="token parameter">obj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> props <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">while</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
     <span class="token comment">// 获取obj对象的所有属性，包括不可枚举的，</span>
    Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      props<span class="token punctuation">[</span>p<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取对象的原型</span>
  <span class="token punctuation">}</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span>
  <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>上面代码依次获取<code>obj</code>对象的每一级原型对象“自身”的属性，从而获取<code>obj</code>对象的“所有”属性，不管是否可遍历。</p> <p>下面是一个例子，列出<code>Date</code>对象的所有属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">inheritedPropertyNames</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span>
<span class="token comment">// [</span>
<span class="token comment">//  &quot;caller&quot;,</span>
<span class="token comment">//  &quot;constructor&quot;,</span>
<span class="token comment">//  &quot;toString&quot;,</span>
<span class="token comment">//  &quot;UTC&quot;,</span>
<span class="token comment">//  ...</span>
<span class="token comment">// ]</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><h3 id="_10、对象的拷贝"><a href="#_10、对象的拷贝" class="header-anchor">#</a> 10、对象的拷贝</h3> <p>如果要拷贝一个对象，需要做到下面两件事情。</p> <ul><li>确保拷贝后的对象，与原对象具有同样的原型。</li> <li>确保拷贝后的对象，与原对象具有同样的实例属性。</li></ul> <p>下面就是根据上面两点，实现的对象拷贝函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">copyObject</span><span class="token punctuation">(</span><span class="token parameter">orig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 拷贝对象函数</span>
    <span class="token comment">// 创建一个新对象，新对象的原型指向旧对象的原型</span>
  <span class="token keyword">var</span> copy <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">copyOwnPropertiesFrom</span><span class="token punctuation">(</span>copy<span class="token punctuation">,</span> orig<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> copy<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">copyOwnPropertiesFrom</span><span class="token punctuation">(</span><span class="token parameter">target<span class="token punctuation">,</span> source</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 拷贝旧对象的实例属性</span>
  Object
    <span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">propKey</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 获取每个属性的 属性描述对象</span>
      <span class="token keyword">var</span> desc <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptor</span><span class="token punctuation">(</span>source<span class="token punctuation">,</span> propKey<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 定义属性，给target对象定义propKey属性，其属性描述对象是desc</span>
      Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> propKey<span class="token punctuation">,</span> desc<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> target<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div><p>另一种更简单的写法，是利用 ES2017 才引入标准的<code>Object.getOwnPropertyDescriptors</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">copyObject</span><span class="token punctuation">(</span><span class="token parameter">orig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>
    Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span><span class="token punctuation">,</span>
    Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptors</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><h2 id="五、严格模式"><a href="#五、严格模式" class="header-anchor">#</a> 五、严格模式</h2> <p>除了正常的运行模式，JavaScript 还有第二种运行模式：严格模式（strict mode）。顾名思义，这种模式采用更加严格的 JavaScript 语法。</p> <p>同样的代码，在正常模式和严格模式中，可能会有不一样的运行结果。一些在正常模式下可以运行的语句，在严格模式下将不能运行。</p> <h3 id="_1、设计目的"><a href="#_1、设计目的" class="header-anchor">#</a> 1、设计目的</h3> <p>早期的 JavaScript 语言有很多设计不合理的地方，但是为了兼容以前的代码，又不能改变老的语法，只能不断添加新的语法，引导程序员使用新语法。</p> <p>严格模式是从 ES5 进入标准的，主要目的有以下几个。</p> <ul><li>明确禁止一些不合理、不严谨的语法，减少 JavaScript 语言的一些怪异行为。</li> <li>增加更多报错的场合，消除代码运行的一些不安全之处，保证代码运行的安全。</li> <li>提高编译器效率，增加运行速度。</li> <li>为未来新版本的 JavaScript 语法做好铺垫。</li></ul> <p>总之，严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向。</p> <h3 id="_2、启用方法"><a href="#_2、启用方法" class="header-anchor">#</a> 2、启用方法</h3> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token string">'use strict'</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><blockquote><p>更多关于严格模式内容：<a href="https://wangdoc.com/javascript/oop/strict.html" target="_blank" rel="noopener noreferrer">https://wangdoc.com/javascript/oop/strict.html<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <h2 id="文档"><a href="#文档" class="header-anchor">#</a> 文档</h2> <p>学习文档：<a href="https://wangdoc.com/javascript/" target="_blank" rel="noopener noreferrer">https://wangdoc.com/javascript/<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></div></div> <div class="page-slot page-slot-bottom"><!-- 横向自适应 -->
      <ins class="adsbygoogle"
          style="display:block"
          data-ad-client="ca-pub-7828333725993554"
          data-ad-slot="6620245489"
          data-ad-format="auto"
          data-full-width-responsive="true"></ins>
      <script>
          (adsbygoogle = window.adsbygoogle || []).push({});
      </script></div> <div class="page-edit"><!----> <div class="tags"><a href="/tags/?tag=JavaScript" title="标签">#JavaScript</a></div> <!----></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="/pages/74d2ab3fbfeaaa68/" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">内置对象</div></a> <a href="/pages/d61b1cb4cdac1f63/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">异步操作</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/pages/74d2ab3fbfeaaa68/" class="prev">内置对象</a></span> <span class="next"><a href="/pages/d61b1cb4cdac1f63/">异步操作</a>→
      </span></p></div></div></div> <div class="article-list"><div class="article-title"><a href="/archives/" class="iconfont icon-bi">最近更新</a></div> <div class="article-wrapper"><dl><dd>01</dd> <dt><a href="/pages/dcedab/"><div>Nginx配置https转http</div></a> <span>12-10</span></dt></dl><dl><dd>02</dd> <dt><a href="/pages/82baa3/"><div>使用State Hook</div></a> <span>04-06</span></dt></dl><dl><dd>03</dd> <dt><a href="/pages/72710d/"><div>使用Effect Hook</div></a> <span>04-06</span></dt></dl> <dl><dd></dd> <dt><a href="/archives/" class="more">更多文章&gt;</a></dt></dl></div></div></main></div> <div class="footer"><!----> 
  Theme by
  <a href="https://github.com/xugaoyi/vuepress-theme-vdoing" target="_blank" title="本站主题">Vdoing</a> 
    | Copyright © 2021-2022
    <span><div style="width:300px;margin:0 auto; padding:20px 0;"><a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=13019902000398" style="display:inline-block;text-decoration:none;height:20px;line-height:20px;"><img src="" style="float:left;"/><p style="float:left;height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;">冀公网安备 13019902000398号</p></a></div></span></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">
          跟随系统
        </li><li class="iconfont icon-rijianmoshi">
          浅色模式
        </li><li class="iconfont icon-yejianmoshi">
          深色模式
        </li><li class="iconfont icon-yuedu">
          阅读模式
        </li></ul></div></div> <div class="body-bg" style="background:url() center center / cover no-repeat;opacity:0.5;"></div> <!----> <div class="custom-html-window custom-html-window-rb" style="display:;"><div class="custom-wrapper"><i class="close-but">×</i> <div><!-- 固定160*160px -->
      <ins class="adsbygoogle"
          style="display:inline-block;max-width:160px;max-height:160px"
          data-ad-client="ca-pub-7828333725993554"
          data-ad-slot="8377369658"></ins>
      <script>
          (adsbygoogle = window.adsbygoogle || []).push({});
      </script>
      </div></div></div></div><div class="global-ui"></div></div>
    <script src="/assets/js/app.ae5bf724.js" defer></script><script src="/assets/js/2.72b09a26.js" defer></script><script src="/assets/js/3.96877024.js" defer></script><script src="/assets/js/134.a455c276.js" defer></script>
  </body>
</html>